Django Signal 源码解析

        Siganl 是 Django 框架中提供的一个 “信号分发器”,其实就是设计模式中经常提到的 观察者模式的一个实现应用。Django 中Siganl 机制的典型应用是,框架为 Models 创建了 pre_save post_save等 与模型的某些方法调用相关联的信号,如 pre_save 和 post_save 分别 会在 Modle 的 save()方法的调用之前和之后 通知观察者。

Signal 机制的实现方式

Siganl的源码位于 django dispatch包下,主要的代码位于 dispatcher.py 中。 
在 dispatcher 中 定义了 Signal 类,以及一个 用于 使用Python装饰器的方式来 连接 信号 以及信号接受者的方法 receiver(signal,**kwargs)

connect方法

   connect 方法 用于 连接 信号 和 信号处理函数,类似的概念 相当于 为 某个事件(信号发出表示一个事件)注册观察者(处理函数), 函数 参数中 receiver 就是 信号处理函数(函数也是对象,这太方便了), 
sender 表示 信号的发送者,比如 Django 框架中的 post_save() 这个 信号,任何一个模型 在save()函数调用之后都会发出这个信号,但是 我们只想关注 某一个模型 save()方法调用的事件发生,就可以指定sender 为 我们需要关注的模型类。 
  weak 参数 表示 是否将 receiver 转换成 弱引用对象,Siganl 中默认 会将所有的 receiver 转成弱引用,所以 如果你的receiver是个局部对象的话,那么receiver 可能会被垃圾回收期回收,receiver 也就变成一个 dead_receiver 了,Siganl 会在 connect 和 disconnect 方法调用的时候,清楚 dead_receiver. 
  dispatch_uid, 这个参数用于唯一标识这个receiver函数,主要的作用是防止 receiver函数被注册多次,这样会导致 receiver函数会执行多次,这可能是我们不想要的一个结果。

disconnect 方法

  disconnect方法 用于 断开 信号的接收器,函数内 首先 会生成 根据 sender 和 receiver 对象构造出的一个 标识 lookup_key,在遍历 receiver数组时,根据lookup_key 找到 需要disconnect 的receiver 然后从数组中删除这个receiver。

send 和 send_robust

send 和 send_robust 方法都是用于发送事件的函数,不同点在于 send_robust 函数中 会捕获 信号接收函数 发生的异常,添加到 返回的 responses数组中。 
“` 
responses = [] 
if not self.receivers or self.sender_receivers_cache.get(sender) is NO_RECEIVERS: 
return responses

    # Call each receiver with whatever arguments it can accept.
    # Return a list of tuple pairs [(receiver, response), ... ].
    for receiver in self._live_receivers(sender):
        try:
            response = receiver(signal=self, sender=sender, **named)
        except Exception as err:
            if not hasattr(err, '__traceback__'):
                err.__traceback__ = sys.exc_info()[2]
            responses.append((receiver, err))
        else:
            responses.append((receiver, response))
    return responses

“`

Siganl类的主要

class Signal(object):
    """
    Base class for all signals

    Internal attributes:

        receivers
            { receiverkey (id) : weakref(receiver) }
    """
    def __init__(self, providing_args=None, use_caching=False):
        """
        创建一个新的Signal
        providing_args 参数,指定这个Siganl 在发出事件(调用send方法)时,可以提供给观察者的信息参数
        比如 post_save()会带上 对应的instance对象,以及update_fields等
        """
        self.receivers = []
        if providing_args is None:
            providing_args = []
        self.providing_args = set(providing_args)
        self.lock = threading.Lock()
        self.use_caching = use_caching
        # For convenience we create empty caches even if they are not used.
        # A note about caching: if use_caching is defined, then for each
        # distinct sender we cache the receivers that sender has in
        # 'sender_receivers_cache'. The cache is cleaned when .connect() or
        # .disconnect() is called and populated on send().
        self.sender_receivers_cache = weakref.WeakKeyDictionary() if use_caching else {}
        self._dead_receivers = False

    def connect(self, receiver, sender=None, weak=True, dispatch_uid=None):

        from django.conf import settings

        if dispatch_uid:
            lookup_key = (dispatch_uid, _make_id(sender))
        else:
            lookup_key = (_make_id(receiver), _make_id(sender))

        if weak:
            ref = weakref.ref
            receiver_object = receiver
            # Check for bound methods
            # 构造弱引用的的receiver
            if hasattr(receiver, '__self__') and hasattr(receiver, '__func__'):
                ref = WeakMethod
                receiver_object = receiver.__self__
            if sys.version_info >= (3, 4):
                receiver = ref(receiver)
                weakref.finalize(receiver_object, self._remove_receiver)
            else:
                receiver = ref(receiver, self._remove_receiver)

        with self.lock:
            #clear掉 由于弱引用 已被垃圾回收期回收的receivers
            self._clear_dead_receivers()
            for r_key, _ in self.receivers:
                if r_key == lookup_key:
                    break
            else:
                self.receivers.append((lookup_key, receiver))
            self.sender_receivers_cache.clear()

    def disconnect(self, receiver=None, sender=None, weak=True, dispatch_uid=None):

        if dispatch_uid:
            lookup_key = (dispatch_uid, _make_id(sender))
        else:
            lookup_key = (_make_id(receiver), _make_id(sender))

        disconnected = False
        with self.lock:
            self._clear_dead_receivers()
            for index in range(len(self.receivers)):
                (r_key, _) = self.receivers[index]
                if r_key == lookup_key:
                    disconnected = True
                    del self.receivers[index]
                    break
            self.sender_receivers_cache.clear()
        return disconnected

    def has_listeners(self, sender=None):
        return bool(self._live_receivers(sender))

    def send(self, sender, **named):

        responses = []
        if not self.receivers or self.sender_receivers_cache.get(sender) is NO_RECEIVERS:
            return responses

        for receiver in self._live_receivers(sender):
            response = receiver(signal=self, sender=sender, **named)
            responses.append((receiver, response))
        return responses

    def send_robust(self, sender, **named):

        responses = []
        if not self.receivers or self.sender_receivers_cache.get(sender) is NO_RECEIVERS:
            return responses

        # Call each receiver with whatever arguments it can accept.
        # Return a list of tuple pairs [(receiver, response), ... ].
        for receiver in self._live_receivers(sender):
            try:
                response = receiver(signal=self, sender=sender, **named)
            except Exception as err:
                if not hasattr(err, '__traceback__'):
                    err.__traceback__ = sys.exc_info()[2]
                responses.append((receiver, err))
            else:
                responses.append((receiver, response))
        return responses

    def _clear_dead_receivers(self):
        # Note: caller is assumed to hold self.lock.
        if self._dead_receivers:
            self._dead_receivers = False
            new_receivers = []
            for r in self.receivers:
                if isinstance(r[1], weakref.ReferenceType) and r[1]() is None:
                    continue
                new_receivers.append(r)
            self.receivers = new_receivers

    def _live_receivers(self, sender):
        """
        过滤掉 已经被 垃圾回收的receiver
        """
        receivers = None
        # 如果使用了cache , 并且没有调用过_remove_receiver 函数 则去 sender_receivers_cache中查找
        if self.use_caching and not self._dead_receivers:
            receivers = self.sender_receivers_cache.get(sender)
            # We could end up here with NO_RECEIVERS even if we do check this case in
            # .send() prior to calling _live_receivers() due to concurrent .send() call.
            if receivers is NO_RECEIVERS:
                return []
        if receivers is None:
            with self.lock:
                self._clear_dead_receivers()
                senderkey = _make_id(sender)
                receivers = []
                for (receiverkey, r_senderkey), receiver in self.receivers:
                    if r_senderkey == NONE_ID or r_senderkey == senderkey:
                        receivers.append(receiver)
                if self.use_caching:
                    if not receivers:
                        self.sender_receivers_cache[sender] = NO_RECEIVERS
                    else:
                        # Note, we must cache the weakref versions.
                        self.sender_receivers_cache[sender] = receivers
        non_weak_receivers = []
        for receiver in receivers:
            if isinstance(receiver, weakref.ReferenceType):
                # Dereference the weak reference.
                receiver = receiver()
                if receiver is not None:
                    non_weak_receivers.append(receiver)
            else:
                non_weak_receivers.append(receiver)
        return non_weak_receivers

    def _remove_receiver(self, receiver=None):

        self._dead_receivers = True
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值