class EventService(Service): def __init__(self, env): """event service""" super(EventService, self).__init__(env) self._channel = Signal('event_channel') def subscribe(self, func, event_type=None ): ''' :param func: def func(event_type, **kwarg): pass :param event_filter: option :return: ''' # sender = event_type or ANY # weak = True # if isinstance(event_type, basestring): # weak = False # self._channel.connect(func, sender, weak) sender = event_type or ANY self._channel.connect(func, sender) def unsubscribe(self, func): self._channel.disconnect(func) def publish(self, event_type, **kwarg): self._channel.send(event_type, **kwarg)
python exapmle05.py disconnect # running signal testing with disconnect one callback # and you can see only one signal callback called Notice: **disconnect** can disconnect receiver from signal’s events. """ import sys from blinker import Signal signal = Signal('disconnect_signal') @signal.connect_via(Signal.ANY) def receive_data(sender, **kw): print("Caught strong signal from : {}, data: {}".format(sender, kw)) return "test" @signal.connect_via(Signal.ANY) def receive_data2(sender, **kw): print("Caught signal2 from : {}, data: {}".format(sender, kw)) return "received2" if __name__ == "__main__": if len(sys.argv) > 1 and sys.argv[1] == 'disconnect': signal.disconnect(receive_data, Signal.ANY) result = signal.send('anonymous', abc=123) print(result)
class SignalEmitter: """Class that allows other callables to connect to it listen for signals. Callables can be attached to a SignalEmitter via SignalEmitter.connect. If the SignalEmitter calls SignalEmitter.signal.send(*args, **kwargs), any callables are called with the respective args and kwargs. Args: initialize_signal: instantiate a blnker.Signal object. If set to False, self.Signal needs to be set later on. This could be useful when you want a single Signal that is shared by all class instances. multiple_senders: Allow to be connected to multiple senders. If False, when connected to a second SignalEmitter, the connection to the previous SignalEmitter is disconnected Note: The SignalEmitter has protection against infinite recursions resulting from signal emitters calling each other. This is done by keeping track of the signal chain. However, it does not protect against infinite recursions from signals sent from objects that are not signal emitters. """ # Signal used for connecting to parameter via SignalEmitter.connect method signal = None def __init__(self, initialize_signal: bool = True, multiple_senders: bool = True): self._signal_chain = [] if initialize_signal: self.signal = Signal() self._signal_modifiers = {'offset': None, 'scale': None} # By default self is not connected to any other SignalEmitter self.sender = None self.multiple_senders = multiple_senders def connect(self, receiver, update=False, offset: float = None, scale: float = None): """Connect a receiver, which can be another SignalEmitter. If a SignalEmitter is passed, the __call__ method is invoked. Args: receiver: Receiver to be connected to this SignalEmitter's signal. offset: Optional offset to apply to emitted value scale: Optional scale to apply to emitted value Note: If offset or scale is provided, the emitted value should be a number. If an emitted signal contains 'value' as kwarg, this will be modified. Otherwise the first arg (sender) will be modified. """ if self.signal is None: self.signal = Signal() if isinstance(receiver, SignalEmitter): # Remove any previous sender if multiple_senders is False if not receiver.multiple_senders and receiver.sender is not None: receiver.sender.disconnect(receiver) receiver._signal_modifiers['offset'] = offset receiver._signal_modifiers['scale'] = scale self.signal.connect(receiver._signal_call) receiver.sender = self else: self.signal.connect(receiver) if update: # Update callable with current value value = self() if scale is not None: if callable(scale): scale = scale(self) value *= scale if offset is not None: if callable(offset): offset = offset(self) value += offset receiver(value) def disconnect(self, callable): """disconnect a callable from a SignalEmitter. Note: Does not raise error if callable is not connected in the first place """ if isinstance(callable, SignalEmitter): callable = callable._signal_call if getattr(self, 'signal', None) is not None: for receiver_ref in list(self.signal.receivers.values()): receiver = receiver_ref() if receiver == callable: self.signal.disconnect(callable) def _signal_call(self, sender, *args, **kwargs): """Method that is called instead of standard __call__ for SignalEmitters This method ensures that the actual __call__ is only invoked if this has not previously been done during the signal chain. """ if self not in self._signal_chain: value = kwargs.get('value', sender) # If any modifier is set, if self._signal_modifiers['scale'] is not None: scale = self._signal_modifiers['scale'] if callable(scale): scale = scale(self.sender) value *= scale if self._signal_modifiers['offset'] is not None: offset = self._signal_modifiers['offset'] if callable(offset): offset = offset(self.sender) value += offset if 'value' in kwargs: kwargs['value'] = value else: sender = value return self(sender, *args, signal_chain=self._signal_chain, **kwargs)