Show
Ignore:
Timestamp:
07/24/2009 12:32:14 AM (12 months ago)
Author:
Friedrich Weber <fred@…>
Children:
d8178792f331c687d0273d1d87ef1e48f518843d
Parents:
571cafadff22c94e7940c41c4edd505ef3872849
git-committer:
Friedrich Weber <fred@samurai-x.org> / 2009-07-24T01:32:14Z+0200
Message:

ooxcb.eventsys: improved support for events defined in base classes

Location:
ooxcb/ooxcb
Files:
2 modified

Legend:

Unmodified
Added
Removed
  • ooxcb/ooxcb/eventsys.py

    r7383ac r91546c  
    2828# Copyright (c) 2006-2008 Alex Holkner 
    2929# All rights reserved. 
    30 #  
     30# 
    3131# Redistribution and use in source and binary forms, with or without 
    32 # modification, are permitted provided that the following conditions  
     32# modification, are permitted provided that the following conditions 
    3333# are met: 
    3434# 
    3535#  * Redistributions of source code must retain the above copyright 
    3636#    notice, this list of conditions and the following disclaimer. 
    37 #  * Redistributions in binary form must reproduce the above copyright  
     37#  * Redistributions in binary form must reproduce the above copyright 
    3838#    notice, this list of conditions and the following disclaimer in 
    3939#    the documentation and/or other materials provided with the 
     
    167167import inspect 
    168168 
    169 EVENT_HANDLED = True  
     169from .util import cached_classproperty 
     170 
     171EVENT_HANDLED = True 
    170172EVENT_UNHANDLED = None 
    171173 
     
    175177    pass 
    176178 
     179class EventDispatcherMeta(type): 
     180    """ 
     181        just a meta class making sure that each EventDispatcher subclass 
     182        has its own `event_types` descriptor. 
     183    """ 
     184    def __new__(mcs, name, bases, dct): 
     185        @cached_classproperty 
     186        def event_types(cls): 
     187            """ 
     188                get the event types of me and all of my bases! 
     189            """ 
     190            types = [] 
     191            for base in cls.__bases__: 
     192                if hasattr(base, 'event_types'): 
     193                    types.extend(base.event_types) 
     194            if hasattr(cls, 'local_event_types'): 
     195                types.extend(cls.local_event_types) 
     196            return types 
     197 
     198        dct['event_types'] = event_types 
     199        return type.__new__(mcs, name, bases, dct) 
     200 
     201 
    177202class EventDispatcher(object): 
    178203    '''Generic event dispatcher interface. 
     
    180205    See the module docstring for usage. 
    181206    ''' 
     207 
     208    __metaclass__ = EventDispatcherMeta 
     209 
    182210    # Placeholder empty stack; real stack is created only if needed 
    183211    _event_stack = () 
     
    196224 
    197225        ''' 
    198         if not hasattr(cls, 'event_types'): 
    199             cls.event_types = [] 
    200         cls.event_types.append(name) 
     226        if not hasattr(cls, 'local_event_types'): 
     227            cls.local_event_types = [] 
     228        cls.local_event_types.append(name) 
    201229        return name 
     230 
    202231 
    203232    def push_handlers(self, *args, **kwargs): 
     
    243272        '''Attach one or more event handlers to the top level of the handler 
    244273        stack. 
    245          
     274 
    246275        See `push_handlers` for the accepted argument types. 
    247276        ''' 
     
    284313        frame, or if no stack frame contains any of the given handlers. 
    285314 
    286         If the stack frame is empty after removing the handlers, it is  
     315        If the stack frame is empty after removing the handlers, it is 
    287316        removed from the stack.  Note that this interferes with the expected 
    288317        symmetry of `push_handlers` and `pop_handlers`. 
     
    343372    def dispatch_event(self, event_type, *args): 
    344373        '''Dispatch a single event to the attached handlers. 
    345          
     374 
    346375        The event is propogated to all handlers from from the top of the stack 
    347376        until one returns `EVENT_HANDLED`.  This method should be used only by 
     
    361390 
    362391        :rtype: bool or None 
    363         :return: (Since pyglet 1.2) `EVENT_HANDLED` if an event handler  
     392        :return: (Since pyglet 1.2) `EVENT_HANDLED` if an event handler 
    364393            returned `EVENT_HANDLED`; `EVENT_UNHANDLED` if one or more event 
    365394            handlers were invoked but returned only `EVENT_UNHANDLED`; 
     
    403432        # arguments in an event handler.  This is caught as a TypeError in 
    404433        # dispatch_event but the error message is obfuscated. 
    405         #  
     434        # 
    406435        # Here we check if there is indeed a mismatch in argument count, 
    407436        # and construct a more useful exception message if so.  If this method 
     
    425454 
    426455        # Allow default values to overspecify arguments 
    427         if (n_handler_args > n_args and  
     456        if (n_handler_args > n_args and 
    428457            handler_defaults and 
    429458            n_handler_args - len(handler_defaults) <= n_args): 
     
    438467            else: 
    439468                descr = repr(handler) 
    440              
     469 
    441470            raise TypeError( 
    442471                '%s event was dispatched with %d arguments, but ' 
    443                 'handler %s has an incompatible function signature' %  
     472                'handler %s has an incompatible function signature' % 
    444473                (event_type, len(args), descr)) 
    445474        else: 
     
    447476 
    448477    def event(self, *args): 
    449         '''Function decorator for an event handler.   
    450          
     478        '''Function decorator for an event handler. 
     479 
    451480        Usage:: 
    452481 
  • ooxcb/ooxcb/util.py

    r4a478d r91546c  
    4646        result = self.func(obj) 
    4747        setattr(obj, self.name, result) 
     48        return result 
     49 
     50class cached_classproperty(object): 
     51    """ 
     52        a modified version of :class:`cached_property` that allows to define 
     53        cached properties on classes. 
     54    """ 
     55    def __init__(self, func): 
     56        self.func = func 
     57        self.name = func.__name__ 
     58 
     59    def __get__(self, obj, type): 
     60        # equal if we call it bound to an instance or not. 
     61        result = self.func(type) 
     62        setattr(type, self.name, result) 
    4863        return result 
    4964