欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

django源码分析:信号signal

程序员文章站 2024-01-19 14:46:10
...

本文环境python3.5.2,django1.10.x系列
本文主要介绍django是如果通过中间件SessionMiddleware来处理session,重点将放到SessionMiddleware中间键的源码讲解。

django本就是依靠一套信号机制来来处理在框架的不同位置之间的信息。完成一套信号的处理函数(receiver),经过初始化后进行储存,等到需要调用此功能的时候,就可以通过发送方(send)将信号(signals)传递给处理函数,并执行得到结果。
我们在下面的讲述中,主要讲述信号是如何发送,如何接收的。

信号系统包含以下三要素:

- 发送者-信号的发出方(send)
- 信号-信号本身(signal)
- 接收者-信号的接受方(receiver)

下面简单看几个????

from django.dispatch import Signal

user_logged_in = Signal(providing_args=['request', 'user'])                # 实例化Signal类,传递不同的参数
user_login_failed = Signal(providing_args=['credentials'])
user_logged_out = Signal(providing_args=['request', 'user'])

这是关于用户登录登出相关的信号处理,这一步中主要完成对Signal类的实例化,下面看看Signal实例化过程的各个参数

1 、信号的建立–signal

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

    Internal attributes:

        receivers
            { receiverkey (id) : weakref(receiver) }
    """
    def __init__(self, providing_args=None, use_caching=False):
        """
        Create a new signal.

        providing_args
            A list of the arguments this signal can pass along in a send() call.
        """
        self.receivers = []                                                       # receivers 处理函数
        if providing_args is None:
            providing_args = []
        self.providing_args = set(providing_args)                                 # 提供参数,参数为list
        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 {}      # 字典,专存储sender和receivers的弱引用
        self._dead_receivers = False                                              # 删除receivers的一个标记

Signal类实例化的主要操作为以上内容,可以详细看每个字段的意义,后面都会用到。

2、信号的接受方–receiver

写一个处理函数如下:

def add_login_record(sender, request, user, **kwargs):
	x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
    ipaddr = x_forwarded_for.split(',')[0]
    device_info = request.META.get('HTTP_USER_AGENT')

这是一个处理并且储存用户请求ip和请求浏览器的方法,作为信号的接收方。也就是信号的处理函数。

下一步就是将这个处理函数添加到signal的处理函数列表中,以登录为例。

user_logged_in.connect(add_login_record)         调用Signal().connect()方法

看一下Signal().connect()方法

    def connect(self, receiver, sender=None, weak=True, dispatch_uid=None):
        """
        Connect receiver to sender for signal.

        Arguments:

            receiver
                A function or an instance method which is to receive signals.
                Receivers must be hashable objects.

                If weak is True, then receiver must be weak referenceable.

                Receivers must be able to accept keyword arguments.

                If a receiver is connected with a dispatch_uid argument, it
                will not be added if another receiver was already connected
                with that dispatch_uid.

            sender
                The sender to which the receiver should respond. Must either be
                a Python object, or None to receive events from any sender.

            weak
                Whether to use weak references to the receiver. By default, the
                module will attempt to use weak references to the receiver
                objects. If this parameter is false, then strong references will
                be used.

            dispatch_uid
                An identifier used to uniquely identify a particular instance of
                a receiver. This will usually be a string, though it may be
                anything hashable.
        """
        from django.conf import settings

        # If DEBUG is on, check that we got a good receiver
        if settings.configured and settings.DEBUG:
            assert callable(receiver), "Signal receivers must be callable."                      # 处理函数必须为可调用

            # Check for **kwargs
            if not func_accepts_kwargs(receiver):
                raise ValueError("Signal receivers must accept keyword arguments (**kwargs).")     # 处理函数必须可以接受**keargs参数

        if dispatch_uid:                                                                         # dispatch_uid为接受器的标识符,如果为空就创建id值
            lookup_key = (dispatch_uid, _make_id(sender))                                         
        else:
            lookup_key = (_make_id(receiver), _make_id(sender))                                  # 为receiver和sender创建唯一id

        if weak:
            ref = weakref.ref                                                                    # 建立弱引用对象
            receiver_object = receiver
            # Check for bound methods
            if hasattr(receiver, '__self__') and hasattr(receiver, '__func__'):
                ref = WeakMethod                                                                 # WeakMethod是django继承python中ref的类,判断是否为类,一般情况我们都是传递函数,所以不会到这一步
                receiver_object = receiver.__self__
            if six.PY3:                                                                          # six.PY3 返回一个表示当前运行环境是否为python3的boolean值
                receiver = ref(receiver)
                weakref.finalize(receiver_object, self._remove_receiver)                         # 建立回调方法
            else:
                receiver = ref(receiver, self._remove_receiver)

        with self.lock:                                                                          # 获取线程锁
            self._clear_dead_receivers()                                                         # 清除前期的无用的receivers
            for r_key, _ in self.receivers:                                                      
                if r_key == lookup_key:
                    break
            else:
                self.receivers.append((lookup_key, receiver))                                    # 将处理函数根据r_key来判断是否已添加到列表中,注意结构,列表中存储的是元组,其中lookup_key又是一个元组
            self.sender_receivers_cache.clear()                                                  # 清除缓存

connect()函数的主要作用是检查接收函数,并且将接收函数添加到self.receivers列表中。用户后面处理信号请求。

3、信号的发送方–send

看完信号的接收者,下面看看发送者,其实发送者也是调用Singal对象的send()方法,看个????

def login(request, user, backend=None):
    """
    Persist a user id and a backend in the request. This way a user doesn't
    have to reauthenticate on every request. Note that data set during
    the anonymous session is retained when the user logs in.
    """
    session_auth_hash = ''
    if user is None:
        user = request.user
    if hasattr(user, 'get_session_auth_hash'):
        session_auth_hash = user.get_session_auth_hash()

    if SESSION_KEY in request.session:
        if _get_user_session_key(request) != user.pk or (
                session_auth_hash and
                not constant_time_compare(request.session.get(HASH_SESSION_KEY, ''), session_auth_hash)):
            # To avoid reusing another user's session, create a new, empty
            # session if the existing session corresponds to a different
            # authenticated user.
            request.session.flush()
    else:
        request.session.cycle_key()

    try:
        backend = backend or user.backend
    except AttributeError:
        backends = _get_backends(return_tuples=True)
        if len(backends) == 1:
            _, backend = backends[0]
        else:
            raise ValueError(
                'You have multiple authentication backends configured and '
                'therefore must provide the `backend` argument or set the '
                '`backend` attribute on the user.'
            )

    request.session[SESSION_KEY] = user._meta.pk.value_to_string(user)
    request.session[BACKEND_SESSION_KEY] = backend
    request.session[HASH_SESSION_KEY] = session_auth_hash
    if hasattr(request, 'user'):
        request.user = user
    rotate_token(request)
    user_logged_in.send(sender=user.__class__, request=request, user=user)                                信号的发送者,在进行登录请求时,发送信号, sender信号为user.__classs__,也就是User class

上段函数是django/contrib/auth/中的login()函数,当完成登录后,调用信号发送,下面具体看一下send()函数。

    def send(self, sender, **named):
        """
        Send signal from sender to all connected receivers.

        If any receiver raises an error, the error propagates back through send,
        terminating the dispatch loop. So it's possible that all receivers
        won't be called if an error is raised.

        Arguments:

            sender
                The sender of the signal. Either a specific object or None.

            named
                Named arguments which will be passed to receivers.

        Returns a list of tuple pairs [(receiver, response), ... ].
        """
        responses = []
        if not self.receivers or self.sender_receivers_cache.get(sender) is NO_RECEIVERS:              # 当处理函数为空或者缓存中也找不到信号时,返回空
            return responses

        for receiver in self._live_receivers(sender):                                                  # 根据sender获得处理函数receiver
            response = receiver(signal=self, sender=sender, **named)                                   # 执行处理函数,获得结果
            responses.append((receiver, response))                                                     # 将结果添加到列表
        return responses


    def _live_receivers(self, sender):
        """
        Filter sequence of receivers to get resolved, live receivers.

        This checks for weak references and resolves them, then returning only
        live receivers.
        """
        receivers = None
        if self.use_caching and not self._dead_receivers:                                       #   判断是否使用缓存,如果使用缓存,则在缓存中找sender
            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)                                                    # 将sender变为id值
                receivers = []
                for (receiverkey, r_senderkey), receiver in self.receivers:                     # 遍历self.receivers, 对比senderkey,将匹配到的ref类添加到列表中
                    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):                                    # ref类实例化获得真正的接受函数,添加到列表中
                # 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

send()函数主要是根据signal信号进行匹配,执行匹配到接收函数。

以上就是关于django信号处理系统的整个代码分析过程,django中的信号使用几乎无处不在,在理解了代码的执行过程后,我们也可以自己编写新的信号处理过程,篇幅有限,在这里就不详述了。希望可以帮助到大家,谢谢