pytilities.event.dispatcher'

Package pytilities :: Package event :: Module dispatcher' |
[hide private]
[frames] | no frames]

# Source Code for Module pytilities.event.dispatcher'

      1  # Copyright (C) 2010 Tim Diels <limyreth@users.sourceforge.net>
      2  #
      3  # This file is part of pytilities.
      4  #
      5  # pytilities is free software: you can redistribute it and/or modify
      6  # it under the terms of the GNU General Public License as published by
      7  # the Free Software Foundation, either version 3 of the License, or
      8  # (at your option) any later version.
      9  #
     10  # pytilities is distributed in the hope that it will be useful,
     11  # but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13  # GNU General Public License for more details.
     14  #
     15  # You should have received a copy of the GNU General Public License
     16  # along with pytilities.  If not, see <http://www.gnu.org/licenses/>.
     17  #
     18
     19  __docformat__ = 'reStructuredText'
     20
     21  from pytilities.delegation import (
     22      delegator_factory, delegated)
     23
     24 -class UnsupportedEventError(Exception):
     25      """Tried to access an unregistered event"""
     26
     27 -    def __init__(self, event_name):
     28          Exception.__init__(self, "Unsupported event: %s" % event_name)
     29
     30  @delegator_factory()
     31 -class Dispatcher(object):
     32
     33      """
     34      Utility class for dispatching events to handlers.
     35
     36      Events have to be registered before they can be dispatched or have handlers
     37      added to them.
     38
     39      Handlers can have an owner associated with them, usually you'll use the
     40      reference of the listener. This allows you to remove all the handlers of a
     41      specific owner, which should save you some work.
     42
     43      Instance methods:
     44
     45          - `add_handler`: Add handler for an event
     46          - `remove_handlers`: Remove all or some handlers
     47          - `remove_handler`: Remove a handler
     48          - `dispatch`: Dispatch an event
     49          - `register_events`: Register events
     50          - `has_event`: Check whether event is supported
     51
     52      Instance properties:
     53
     54          - `events`: Read-only, set of all supported events
     55
     56      Instance decorators:
     57
     58          - `event`: Register decorated as a handler
     59
     60      Class invariants:
     61
     62          - For every (owner, event), there can be only 0 or 1 handlers
     63      """
     64
     65      # inspired by pyglet.event.EventDispatcher, yay pyglet
     66
     67      @staticmethod
     68 -    def __init_delegation_profiles(profiles):
     69          profiles['default'] |= profiles['public']
     70
     71 -    def __init__(self):
     72          # dict of handlers: event_name -> set((handler, owner))
     73          self.__handlers = {}
     74          self.__registered_events = set()
     75
     76      @delegated("public")
     77 -    def add_handler(self, event_name, handler, owner = None):
     78          """
     79          Add handler for an event, optionally with an owner.
     80
     81          Parameters:
     82
     83              event_name :: string
     84                  name of the event to add the handler to
     85
     86              handler :: callable
     87                  the handler to call when the event is dispatched
     88
     89              owner = None
     90                  owner of the handler. Use this reference to easily remove all
     91                  your handlers from the dispatcher (e.g. remove all handlers
     92                  with the same owner)
     93
     94          Raises:
     95
     96              - `UnsupportedEventError` when `event_name` doesn't exist
     97          """
     98
     99          if not self.has_event(event_name):
    100              raise UnsupportedEventError(event_name)
    101
    102          assert handler, "handler argument musn't be None"
    103
    104          assert (handler, owner) not in self.__handlers, (
    105              "You cannot add the same handler for the same owner and event " +
    106              "more than once.")
    107
    108          self.__handlers.setdefault(event_name, set())
    109          self.__handlers[event_name].add((handler, owner))
    110
    111      @delegated("public")
    112 -    def remove_handlers(self, event_name=None, owner=None):
    113          """
    114          Remove all or some handlers of the dispatcher.
    115
    116          `event_name` and `owner` act as filters of what to remove.
    117
    118          If no handler matched the criterea, the method will return silently.
    119
    120          Parameters:
    121
    122              event_name :: string = None
    123                  the event of which to remove the handlers. `None` means any
    124
    125              owner = None
    126                  the owner of which to remove the handlers. `None` means any
    127          """
    128
    129          if event_name:
    130              self.__remove_handlers(event_name, owner)
    131          else:
    132              for event in self.__registered_events:
    133                  self.__remove_handlers(event, owner)
    134
    135 -    def __remove_handlers(self, event, owner):
    136          """Remove handlers of single event, optionally with particular owner """
    137          handlers = self.__handlers[event]
    138          handlers -= set((handler, o) for (handler, o) in handlers
    139                          if o is owner)
    140
    141      @delegated("public")
    142 -    def remove_handler(self, event_name, handler, owner = None):
    143          """
    144          Remove a handler from an event. It is an error to try to remove a
    145          handler from an event that doesn't have this handler attached to it.
    146
    147          Parameters:
    148
    149              event_name :: string
    150                  name of the event to which the handler belongs
    151
    152              handler :: callable
    153                  the handler that is attached to the event
    154
    155              owner = None
    156                  owner of the handler
    157
    158          Preconditions:
    159              1. `handler` is attached to `event_name`
    160
    161          Raises:
    162              - `UnsupportedEventError` when `event_name` doesn't exist
    163          """
    164
    165          if not self.has_event(event_name):
    166              raise UnsupportedEventError(event_name)
    167
    168          assert handler, "handler argument musn't be None"
    169
    170          self.__handlers[event_name].remove((handler, owner))
    171
    172      @delegated()
    173 -    def dispatch(self, event_name, *args, **keyword_args):
    174          """
    175          Dispatch an event to its handlers.
    176
    177          The handlers are executed in a random order.
    178
    179          Parameters:
    180
    181              event_name :: string
    182                  name of the event to dispatch
    183
    184              args
    185                  arguments to pass to the handlers
    186
    187              keyword_args
    188                  keyword arguments to pass to the handlers
    189
    190          Raises:
    191              - `UnsupportedEventError` when `event_name` doesn't exist
    192          """
    193
    194          if not self.has_event(event_name):
    195              raise UnsupportedEventError(event_name)
    196
    197          for handler in self.__handlers.get(event_name, ()):
    198              handler[0](*args, **keyword_args)
    199
    200      @delegated("public")
    201 -    def event(self, event_name, owner = None):
    202          """
    203          Register the decorated as a handler of `event_name`
    204
    205          Parameters:
    206
    207              event_name :: string
    208                  name of the event
    209
    210              owner = None
    211                  owner of the handler. Use this reference to easily remove all
    212                  your handlers from the dispatcher (e.g. remove all handlers
    213                  with the same owner)
    214          """
    215
    216          def decorator(handler):
    217              self.add_handler(event_name, handler, owner)
    218              return handler
    219
    220          return decorator
    221
    222      @delegated()
    223 -    def register_events(self, *event_names):
    224          """
    225          Register events.
    226
    227          Parameters:
    228
    229              event_names :: (string...)
    230                  names of events to support
    231          """
    232          # this allows us to have methods like has event, which allow for better
    233          # duck typing (if I see something that sends out quack events, then I
    234          # call that a duck)
    235          self.__registered_events.update(event_names)
    236
    237      @delegated("public")
    238 -    def has_event(self, event_name):
    239          """
    240          Checks if `event_name` is supported
    241
    242          Parameters:
    243
    244              event_name :: string
    245                  name of the event
    246
    247          Returns True if the dispatcher has the event
    248          """
    249          return event_name in self.__registered_events
    250
    251      @delegated("public")
    252      @property
    253 -    def events(self):
    254          """
    255          Read-only, set of all supported events
    256
    257          Returns ::frozenset(string...)
    258          """
    259          return frozenset(self.__registered_events)
    260