def disconnect(self, receiver, sender=ANY): """Disconnect *receiver* from this signal's events.""" if sender is ANY: sender_id = ANY_ID else: sender_id = hashable_identity(sender) receiver_id = hashable_identity(receiver) self._disconnect(receiver_id, sender_id)
def connect(self, receiver, sender=ANY, weak=True): """Connect *receiver* to signal events sent by *sender*. :param receiver: A callable. Will be invoked by :meth:`send` with `sender=` as a single positional argument and any \*\*kwargs that were provided to a call to :meth:`send`. :param sender: Any object or :obj:`ANY`, defaults to ``ANY``. Restricts notifications delivered to *receiver* to only those :meth:`send` emissions sent by *sender*. If ``ANY``, the receiver will always be notified. A *receiver* may be connected to multiple *sender* values on the same Signal through multiple calls to :meth:`connect`. :param weak: If true, the Signal will hold a weakref to *receiver* and automatically disconnect when *receiver* goes out of scope or is garbage collected. Defaults to True. """ receiver_id = hashable_identity(receiver) if weak: receiver_ref = reference(receiver, self._cleanup_receiver) receiver_ref.receiver_id = receiver_id else: receiver_ref = receiver if sender is ANY: sender_id = ANY_ID else: sender_id = hashable_identity(sender) self.receivers.setdefault(receiver_id, receiver_ref) self._by_sender[sender_id].add(receiver_id) self._by_receiver[receiver_id].add(sender_id) del receiver_ref if sender is not ANY and sender_id not in self._weak_senders: # wire together a cleanup for weakref-able senders try: sender_ref = reference(sender, self._cleanup_sender) sender_ref.sender_id = sender_id except TypeError: pass else: self._weak_senders.setdefault(sender_id, sender_ref) del sender_ref # broadcast this connection. if receivers raise, disconnect. if receiver_connected.receivers and self is not receiver_connected: try: receiver_connected.send(self, receiver_arg=receiver, sender_arg=sender, weak_arg=weak) except: self.disconnect(receiver, sender) raise return receiver
def disconnect(self, receiver, sender=ANY): """Disconnect *receiver* from this signal's events. :param receiver: a previously :meth:`connected<connect>` callable :param sender: a specific sender to disconnect from, or :obj:`ANY` to disconnect from all senders. Defaults to ``ANY``. """ if sender is ANY: sender_id = ANY_ID else: sender_id = hashable_identity(sender) receiver_id = hashable_identity(receiver) self._disconnect(receiver_id, sender_id)
def connect(self, receiver, sender=ANY, weak=True, scheduler=None): """Connect *receiver* to signal events sent by *sender*. :param receiver: A callable. Will be invoked by :meth:`send` with `sender=` as a single positional argument and any \*\*kwargs that were provided to a call to :meth:`send`. :param sender: Any object or :obj:`ANY`, defaults to ``ANY``. Restricts notifications delivered to *receiver* to only those :meth:`send` emissions sent by *sender*. If ``ANY``, the receiver will always be notified. A *receiver* may be connected to multiple *sender* values on the same Signal through multiple calls to :meth:`connect`. :param weak: If true, the Signal will hold a weakref to *receiver* and automatically disconnect when *receiver* goes out of scope or is garbage collected. Defaults to True. :param scheduler: Callable used to ensure the execution of a future. Note that if you do not provide an ``schedule`` and ``reciever`` is a function decorated with :func:`asyncio.coroutine`, :func:`asyncio.ensure_future` will be used. """ reciever = super().connect(receiver, sender, weak) reciever_id = hashable_identity(receiver) if scheduler or asyncio.coroutines.iscoroutinefunction(receiver): if not scheduler: scheduler = ensure_future self._recievers_schedulers[reciever_id] = scheduler return reciever
def disconnect(self, receiver): """Disconnect *receiver* from this signal's events. :param receiver: a previously :meth:`connected<connect>` callable """ receiver_id = hashable_identity(receiver) self._disconnect(receiver_id)
def receivers_for(self, sender): """Iterate all live receivers listening for *sender*.""" # TODO: test receivers_for(ANY) if self.receivers: sender_id = hashable_identity(sender) if sender_id in self._by_sender: ids = ( self._by_sender[ANY_ID] | # _by_sender 映射 senderid -> receiverids self._by_sender[sender_id] ) # question : 这里对两个 id set做或操作意图是? # 每个sender 应该对应的 receivers 不应该只是明确订阅是自己的 receiver # 还应该包括 对应 ANY 的receivers,所以这里对 set 做了 或操作 # ps: {1,2,3} | {2,3,4} == {1,2,3,4} else: # 对应 sender_id 不存在 _by_sender 中的情况。这种情况 可能 对应用户自己输错 sender todo ids = self._by_sender[ANY_ID].copy() for receiver_id in ids: receiver = self.receivers.get(receiver_id) if receiver is None: continue if isinstance(receiver, WeakTypes): strong = receiver() if strong is None: self._disconnect(receiver_id, ANY_ID) continue receiver = strong yield receiver
def has_receivers_for(self, sender): """True if there is probably a receiver for *sender*. Performs an optimistic check only. Does not guarantee that all weakly referenced receivers are still alive. See :meth:`receivers_for` for a stronger search. """ if not self.receivers: return False if self._by_sender[ANY_ID]: return True if sender is ANY: return False return hashable_identity(sender) in self._by_sender
def send(self, *sender, **kwargs): """ Emit this signal on behalf of *sender*, passing on \*\*kwargs. Returns a list of 2-tuples, pairing receivers with their return value. If receiver is a coroutine the return value is a Future. The ordering of receiver notification is undefined. :param \*sender: Any object or ``None``. If omitted, synonymous with ``None``. Only accepts one positional argument. :param \*\*kwargs: Data to be sent to receivers. """ return [ (reciever, self._recievers_schedulers[hashable_identity(reciever)](value)) for reciever, value in super().send(*sender, **kwargs) ]
def connect(self, receiver, weak=True): """\ Connect a signal receiver to me. `proc` will be called with two positional arguments: the destination object and the signal that's transmitted. Whatever keywords args the sender set in its .send() call are passed as-is. """ if not hasattr(self,'_receivers'): self._receivers = dict() receiver_id = hashable_identity(receiver) if weak: receiver_ref = reference(receiver, self._cleanup_receiver) receiver_ref.receiver_id = receiver_id else: receiver_ref = receiver self._receivers.setdefault(receiver_id, receiver_ref)
def receivers_for(self, sender): """Iterate all live receivers listening for *sender*.""" # TODO: test receivers_for(ANY) if self.receivers: sender_id = hashable_identity(sender) if sender_id in self._by_sender: ids = (self._by_sender[ANY_ID] | self._by_sender[sender_id]) else: ids = self._by_sender[ANY_ID].copy() for receiver_id in ids: receiver = self.receivers.get(receiver_id) if receiver is None: continue if isinstance(receiver, WeakTypes): strong = receiver() if strong is None: self._disconnect(receiver_id, ANY_ID) continue receiver = strong yield receiver
def receivers_for(self, sender): """Iterate all live receivers listening for *sender*.""" # TOXDO: test receivers_for(ANY) if self.receivers: sender_id = hashable_identity(sender) if sender_id in self._by_sender: ids = (self._by_sender[ANY_ID] | self._by_sender[sender_id]) else: ids = self._by_sender[ANY_ID].copy() for receiver_id in ids: receiver = self.receivers.get(receiver_id) if receiver is None: continue if isinstance(receiver, WeakTypes): strong = receiver() if strong is None: self._disconnect(receiver_id, ANY_ID) continue receiver = strong yield receiver
def connect(self, receiver, sender=ANY, weak=True): """Connect *receiver* to signal events send by *sender*. :param receiver: A callable. Will be invoked by :meth:`send`. Will be invoked with `sender=` as a named argument and any \*\*kwargs that were provided to a call to :meth:`send`. :param sender: Any object or :attr:`Signal.ANY`. Restricts notifications to *receiver* to only those :meth:`send` emissions sent by *sender*. If ``ANY``, the receiver will always be notified. A *receiver* may be connected to multiple *sender* on the same Signal. Defaults to ``ANY``. :param weak: If true, the Signal will hold a weakref to *receiver* and automatically disconnect when *receiver* goes out of scope or is garbage collected. Defaults to True. """ receiver_id = hashable_identity(receiver) if weak: receiver_ref = reference(receiver, self._cleanup_receiver) receiver_ref.receiver_id = receiver_id else: receiver_ref = receiver if sender is ANY: sender_id = ANY_ID else: sender_id = hashable_identity(sender) # 主要需要登记 receiver_id, sender_id, receiver_ref # receiver_ref 对应两种情况 1. 当要求存入弱引用时(选项weak 为 true,需要对传入的引用做一次弱引用的封装) # 2. 选项为false时,直接赋值为原值就好了 # receiver的引用保存在 receivers dict 中,通过receiver_id进行查询 self.receivers.setdefault(receiver_id, receiver_ref) # _by_sender 字典用来保存对应于每个sender的receiver 订阅者 # _by_receiver 字典相反,用来保存对于每个 receiver 订阅者的 sender self._by_sender[sender_id].add(receiver_id) self._by_receiver[receiver_id].add(sender_id) # todo 这个del 很奇怪,每次函数结束receiver_ref是会被自动删除的才对,为什么要自行del del receiver_ref if sender is not ANY and sender_id not in self._weak_senders: # wire together a cleanup for weakref-able senders try: sender_ref = reference(sender, self._cleanup_sender) sender_ref.sender_id = sender_id except TypeError: pass # 第一次碰到 try-except中的else。这个else对应于 没有exception抛出的情况 要执行的内容 else: self._weak_senders.setdefault(sender_id, sender_ref) del sender_ref # broadcast this connection. if receivers raise, disconnect. # todo receiver_connected 的作用? # 每次对任意一个Signal(当然receriver_connected Signal除外), # 都会触发receiver_connected信号,我们可以对receiver_connected 信号进行订阅,做一些有用的事情,比如记录每次信号触发的信息 if receiver_connected.receivers and self is not receiver_connected: # 判断 self is not receiver_connected 十分重要,不然会引起死循环 try: receiver_connected.send( self, # sender 是Signal自己 receiver_arg= receiver, # 下面的args 是记录的信号收发的信息,三要素:收信人,寄信人,是否要求弱引用 sender_arg=sender, weak_arg=weak) except: self.disconnect(receiver, sender) raise return receiver