django源码分析:信号signal
本文环境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中的信号使用几乎无处不在,在理解了代码的执行过程后,我们也可以自己编写新的信号处理过程,篇幅有限,在这里就不详述了。希望可以帮助到大家,谢谢
上一篇: 人生中的第一篇技术博客
下一篇: Go嵌套并发实现EDM,附坑点分析#1
推荐阅读