def __init__(self): for signal in self.__signals__.values(): ctl = signal.default_control(self) setattr(self, "_%s_ctl" % pyname(signal.name), ctl) if not isinstance(signal, DelegateSignal): setattr(self, "_%s_book" % pyname(signal.name), ctl._runner) # Registra os métodos criados pelo mecanismo on_signal... auto_method = getattr(self, "on_" + pyname(signal.name), None) if auto_method is not None: kwargs = {"owner": self} if signal._num_filters == 0: self.listen(signal.name, auto_method, **kwargs) else: args = (None,) * signal._num_filters args += (auto_method,) self.listen(signal.name, *args, **kwargs) # Registra os métodos criados pelo mecanismo @listen marked_listen = self.__marked_listen__.get(signal.name, []) for (func_name, args, kwargs) in marked_listen: handler = getattr(self, func_name) filters = args[: signal._num_filters] or (None,) * signal._num_filters args = args[signal._num_filters :] args = filters + (handler,) + args kwargs = kwargs.copy() kwargs["owner"] = self self.listen(signal.name, *args, **kwargs)
def new_signal(self, name, *filters, **kwds): '''Cria um novo sinal para o evento global. Possui uma assinatura variável onde o tipo de sinal é escolhido automaticamente a partir dos argumentos signal('foo', num_args=1) Sinal simples chamado 'foo' com apenas 1 argumento. signal('foo', 'bar') Sinal filtrado chamado 'foo' que requer um argumento extra 'bar' signal('foo', delegate_to=obj) Delega sinal 'foo' para o objeto fornecido em *delegate_to* ''' cls = type(self) attr = pyname(name) # Organiza delegações e salva delegado em atributo if 'delegate_to' in kwds: delegate_obj = kwds['delegate_to'] try: delegate_attr = self._delegates[id(delegate_obj)] except KeyError: delegate_attr = '_delegate_%s' % self._delegate_idx self._delegates[id(delegate_obj)] = delegate_attr self._delegate_idx += 1 setattr(self, delegate_attr, delegate_obj) kwds['delegate_to'] = delegate_attr # Cria sinal signal_obj = signal(name, *filters, **kwds) if name in cls.__signals__: raise ValueError('global signal already exists: %r' % name) cls.__signals__[attr] = signal_obj setattr(cls, pyname(name), signal_obj) # Cria os métodos do tipo listen_<> e tipo trigger_<> lname = 'listen_' + attr tname = 'trigger_' + attr listen_method = signal_obj._factory_listen_method() trigger_method = signal_obj._factory_trigger_method() setattr(cls, lname, listen_method) setattr(cls, tname, trigger_method) # Inicializa SignalCtl ctl = signal_obj.default_control() setattr(self, '_%s_ctl' % attr, ctl) if not isinstance(signal, DelegateSignal): setattr(self, '_%s_book' % attr, ctl._runner)
def _factory_trigger_method(self): signal = self # não confundir com o self da função fabricada! attr_name = pyname('_%s_book' % signal.name) # Especializamos para 0, 1 ou 2 argumentos por questões de performance if signal.num_args == 0: def trigger_method(self, key): book = getattr(self, attr_name) kbook = book.get(key, None) if kbook is not None: for wrapped_func in kbook: wrapped_func() else: gen_book = book.get(None, None) if gen_book is not None: for func in gen_book: func(key) elif signal.num_args == 1: def trigger_method(self, key, arg): book = getattr(self, attr_name) kbook = book.get(key, None) if kbook is not None: for wrapped_func in kbook: wrapped_func(arg) else: gen_book = book.get(None, None) if gen_book is not None: for func in gen_book: func(key, arg) else: def trigger_method(self, key, *args): if len(args) != signal.num_args: raise TypeError('expected %s arguments' % self.num_args) book = getattr(self, attr_name) kbook = book.get(key, None) if kbook is not None: for wrapped_func in kbook: wrapped_func(*args) else: gen_book = book.get(None, None) if gen_book is not None: for func in gen_book: func(key, *args) trigger_method.__name__ = 'trigger_' + pyname(signal.name) # trigger_method.__doc__ = (trigger_method.__doc__ # % {'signal': signal.name}) return trigger_method
def _populate_namespace(cls, name, bases, ns): """Retorna o namespace ns acrescentado dos métodos trigger_* e listen_* apropriados para a classe em questão""" ns = dict(ns) # Lê os sinais e monta um dicionário de sinais signals = {} for C in reversed(bases): signals.update(getattr(C, "__signals__", {})) for attr, value in ns.items(): if isinstance(value, Signal): signals[attr] = value ns["__signals__"] = signals # Cria os métodos do tipo listen_* e tipo trigger_* for signal in signals.values(): lname = "listen_" + pyname(signal.name) tname = "trigger_" + pyname(signal.name) ns["_" + lname] = signal._factory_listen_method() if lname not in ns: ns[lname] = ns["_" + lname] ns["_" + tname] = signal._factory_trigger_method() if tname not in ns: ns[tname] = ns["_" + tname] # Escaneia todos os métodos decorados com @listen listen = {} for C in reversed(bases): listen.update(getattr(C, "__marked_listen__", {})) for attr, value in ns.items(): if hasattr(value, "_listen_args"): for name, args, kwds in getattr(value, "_listen_args"): L = listen.setdefault(name, []) L.append((attr, args, kwds)) ns["__marked_listen__"] = listen # Atribui valores de slots, caso necessário if "__slots__" in ns: slots = list(ns["__slots__"]) for signal_name, signal in signals.items(): if not isinstance(signal, DelegateSignal): slots.append("_%s_ctl" % signal_name) slots.append("_%s_book" % signal_name) ns["__slots__"] = slots return ns
def _factory_trigger_method(self): signal = self # não confundir com o self da função fabricada! attr_name = pyname('_%s_book' % signal.name) # Especializamos para 0, 1 ou 2 argumentos por questões de performance if signal.num_args == 0: def trigger_method(self): book = getattr(self, attr_name) for wrapped_func in book: wrapped_func() elif signal.num_args == 1: def trigger_method(self, arg): book = getattr(self, attr_name) for wrapped_func in book: wrapped_func(arg) elif signal.num_args == 2: def trigger_method(self, arg1, arg2): book = getattr(self, attr_name) for wrapped_func in book: wrapped_func(arg1, arg2) else: def trigger_method(self, *args): if len(args) != signal.num_args: raise TypeError('expected %s arguments' % self.num_args) book = getattr(self, attr_name) for wrapped_func in book: wrapped_func(*args) trigger_method.__name__ = 'trigger_' + pyname(signal.name) # trigger_method.__doc__ = trigger_method.__doc__ % \ # {'signal': signal.name} return trigger_method
def listen(self, signal, *args, **kwds): """Registra um handler para um determinado sinal. A assinatura correta para esta função depende do tipo de sinal considerado. Para sinais simples, basta utilizar:: obj.listen(<nome do sinal>, <handler>) Sinais filtrados (aqueles que pedem um ou mais argumentos adicionais), devem ser registrados como:: obj.listen(<nome do sinal>, <filtro>, <handler>) Opcionalmente, é possível acrescentar argumentos por nome ou posição que serão passados automaticamente para o handler quando o sinal for acionado. """ implementation = getattr(self, "listen_" + pyname(signal)) return implementation(*args, **kwds)
def listen_method(self, *args, **kwargs): '''vsds %(signal)s''' # Confere argumentos de entrada args_ = kwargs.pop('args', None) kwargs_ = kwargs.pop('kwargs', None) owner = kwargs.pop('owner', None) if args_: args = args_ if kwargs_: kwargs = kwargs_ # Extrai os filtros, caso necessário filter_args = args[:signal._num_filters] try: handler = args[signal._num_filters] except IndexError: handler = None args = args[signal._num_filters + 1:] if args_ is not None and args: raise TypeError('cannot specify the args parameter and also ' 'insert positional arguments') if kwargs_ is not None and kwargs: raise TypeError('cannot specify the kwargs parameter and also ' 'insert keyword arguments') # Caso handler=None, estamos chamando o método listen na versão # de decorador if handler is None: def decorator(func): args_ = filter_args + (func,) kwargs_ = {'args': args, 'kwargs': kwargs, 'owner': owner} return listen_method(self, *args_, **kwargs_) return decorator # Registra nas listas de controle wrapped = wrap_handler( handler, args, kwargs, pre_args=signal.num_args) ctl = getattr(self, '_%s_ctl' % pyname(signal.name)) if signal._num_filters == 0: idx = len(ctl._data) elem = ControlElem(idx, owner, wrapped, handler, args, kwargs) ctl._runner.append(wrapped) ctl._data.append(elem) return idx elif signal._num_filters == 1: key = filter_args[0] ids = {} if key is None: # Registra como handler de todas os filtros no livro for k in ctl._runner: if k is None: continue wrapped_i = wrap_handler(handler, (k,) + args, kwargs, pre_args=signal.num_args) ids[k] = listen_method(self, k, wrapped_i) # Cria um elemento de controle substituindo o campo # "wrapped" pelo dicionário das ids em cada filtro control = ctl._data.setdefault(None, []) book = ctl._runner.setdefault(None, []) idx = len(control) elem = ControlElem(idx, owner, ids, handler, args, kwargs) control.append(elem) book.append( wrap_handler( handler, args, kwargs, pre_args=signal.num_args)) return (None, idx) else: try: idx = len(ctl._data[key]) except KeyError: ctl._data[key] = [] ctl._runner[key] = [] for ctl_e in ctl._data.get(None, []): wrapped_i = wrap_handler( ctl_e.handler, (key,) + ctl_e.args, ctl_e.kwargs, signal.num_args) ctl_e.wrapped[key] = listen_method( self, key, wrapped_i) idx = len(ctl._data[key]) elem = ControlElem( idx, owner, wrapped, handler, args, kwargs) ctl._runner[key].append(wrapped) ctl._data[key].append(elem) return key, idx else: book = ctl._runner[filter_args] control = ctl._data[filter_args] raise RuntimeError
def __init__(self, name, num_args=0): self.name = name self.num_args = num_args self._num_filters = 0 self._ctl_name = '_%s_ctl' % pyname(self.name)
def trigger(self, signal, *args, **kwds): """Aciona o sinal com os argumentos fornecidos.""" implementation = getattr(self, "trigger_" + pyname(signal)) return implementation(*args, **kwds)