【Flask源码解读】 Context 解析

在使用 Flask 的过程中,有一项很舒服的功能不知道小伙伴们有没有发现,那么就是我们写 View 的时候居然不用传递 requestresponse 变量,例如直接这些写就好了:

context_sample_code.png

但是,用过 Django 和 Tornado 的同学也都知道,在 Django 中是需要我们自己传递 request 对象的,就像这样:

context_django_example.png

Tornado 里面虽然没有传递这样的 Request 变量,但是,Tornado 却是需要集成自 RequestHandler 类,其实也就是变相得传递了。

那么为什么都是 Web 框架,而 Flask 却可以做到不需要传递 request 呢?这其实就是这篇文章要解析得 Context 上下文。而且这个 Context 不仅影响 request, 还会影响全局变量 g 和一些参数。

什么是上下文

从 Flask 的官方文档中,我们可以发现在 Flask 中,Context 分为两类,分别是 AppContextRequestContext。其中 RequestCntext 是只针对于单次请求有效的,也就是我们常用的 requestsession 这类;另外一种 AppContext 是针对于整个应用周期的,可以理解为 Flask 的 Web 应用一启动就有,关闭就结束的一种 Context,例如我们常用的 gcurrent_app 变量这一类。

Context 如何起作用

为了理解 Context 是如何起作用的,我们还是来跟踪一下源代码看看,打开 flask/globals.py 这个文件,可以看到以下的内容:

flask/globals.py

context_flask_global_code_1.png

这里可以发现,所有的这些变量都跟 Stack 有关,而Stack 也分为两种,分别是:

line 56: _request_ctx_stack = LocalStack()
line 57: _app_ctx_stack = LocalStack()

何时入栈 出栈

flask/app.py

line 1936: def wsgi_app(self, environ, start_response):
... ...
line 1961:     ctx = self.request_context(environ)
line 1961:     ctx.push()
line 1961:     error = None
line 1961:     try:
line 1961:         try:
line 1961:          response = self.full_dispatch_request()
line 1961:      except Exception as e:
line 1961:           error = e
line 1961:            response = self.make_response(self.handle_exception(e))
line 1961:        return response(environ, start_response)
line 1961:     finally:
line 1961:         if self.should_ignore_error(error):
line 1961:           error = None
line 1961:      ctx.auto_pop(error)

这里可以发现其实是每次请求过来的时候就入栈了,然后请求完成之后就出栈了。

有没有可能栈里面有多个元素

栈里面不会出现多个元素,但是,在调试模式下,可能在一个请求出错之后元素不会出栈,代码在:

flask/ctx.py

line  377:    def auto_pop(self, exc):
line  378:        if self.request.environ.get('flask._preserve_context') or \
line  379:           (exc is not None and self.app.preserve_context_on_exception):
line  380:            self.preserved = True
line  381:            self._preserved_exc = exc
line  382:        else:
line  383:            self.pop(exc)

然后在下一次的请求入栈前先出栈

line  297:    def push(self):

... ...
line  307:       top = _request_ctx_stack.top
line  308:       if top is not None and top.preserved:
line  309:            top.pop(top._preserved_exc)


遗留问题:如果不会出现多个元素干嘛要用栈?

Context 里面有什么内容

  • RequestContext

    request context 包含所有请求相关的信息。 可以直接使用的有:

    • app
    • request
    • url_adapter
    • flashes
    • session

· EOF ·

最近爬虫很是凶猛,标注下文章的原文地址: https://liuliqiang.info/post/flask-source-analysis-context/