资讯专栏INFORMATION COLUMN

深入理解flask框架(2):应用上下文与请求上下文

wushuiyong / 1108人阅读

摘要:实现一个进程中拥有多个应用上下文机制依赖的数据结构上下文机制的实现基于的。

什么是上下文?

flask框架中的上下文本质上就是两个类,我们可以先看一下他的初始化函数:
应用上下文

class AppContext(object):
    """The application context binds an application object implicitly
    to the current thread or greenlet, similar to how the
    :class:`RequestContext` binds request information.  The application
    context is also implicitly created if a request context is created
    but the application is not on top of the individual application
    context.
    """

    def __init__(self, app):
        self.app = app
        self.url_adapter = app.create_url_adapter(None)
        self.g = app.app_ctx_globals_class()

        # Like request context, app contexts can be pushed multiple times
        # but there a basic "refcount" is enough to track them.
        self._refcnt = 0

请求上下文

class RequestContext(object):
    def __init__(self, app, environ, request=None):
        self.app = app
        if request is None:
            request = app.request_class(environ)
        self.request = request
        self.url_adapter = app.create_url_adapter(self.request)
        self.flashes = None
        self.session = None
        self._implicit_app_ctx_stack = []
        self.preserved = False
        self._preserved_exc = None
        self._after_request_functions = []
        self.match_request()
为什么设计上下文这样的机制?

详细解释可参考:
https://blog.tonyseek.com/pos...

多线程环境下,实现线程之间的隔离

类似Thread Local ,每个线程对一个 Thread Local 对象的修改都不会影响其他线程。这种对象的实现原理也非常简单,只要以线程的 ID 来保存多份状态字典即可,就像按照门牌号隔开的一格一格的信箱。

实现一个 Python 进程中拥有多个应用

from werkzeug.wsgi import DispatcherMiddleware
from biubiu.app import create_app
from biubiu.admin.app import create_app as create_admin_app

application = DispatcherMiddleware(create_app(), {
    "/admin": create_admin_app()
})
上下文机制依赖的数据结构

flask上下文机制的实现基于 Werkzeug 的 Local Stack 。
阅读源码,Local Stack又依赖于local类,我们发现Local类的本质是一个字典和一个获取到线程id的函数。

class Local(object):
    __slots__ = ("__storage__", "__ident_func__")

    def __init__(self):
        object.__setattr__(self, "__storage__", {})
        object.__setattr__(self, "__ident_func__", get_ident)

而LocalStack在Local类的基础上又实现了栈的功能。

class LocalStack(object):
        def __init__(self):
        self._local = Local()

flask中应用上下文栈和请求上下文栈正是基于上面的LocalStack类

_request_ctx_stack = LocalStack()
_app_ctx_stack = LocalStack()

下面我们通过一些实验来进一步学习flask的执行过程:

In [1]:  from flask.globals import _app_ctx_stack, _request_ctx_stack

In [2]: from flask import Flask

In [3]: app = Flask(__name__)

In [4]: _app_ctx_stack._local.__storage__
Out[4]: {}

In [5]: _request_ctx_stack._local.__storage__
Out[5]: {}

In [6]: req_ctx = app.test_request_context()

In [7]: req_ctx.push()

In [8]: _request_ctx_stack._local.__storage__
Out[8]: {: {"stack": []}}

In [9]: _app_ctx_stack._local.__storage__
Out[9]: {: {"stack": []}}

我们可以看到一开始上下文均为空,test_request_context()函数会生成一个请求上下文,我们通过push让它入栈,之后两个上下文都有了内容,为什么_app_ctx_stack中也有内容呢?
我们可以看一下源码,第一次请求上下文push时,app_ctx如果为None,就会调用_implicit_app_ctx_stack_添加一个应用上下文。

        app_ctx = _app_ctx_stack.top
        if app_ctx is None or app_ctx.app != self.app:
            app_ctx = self.app.app_context()
            app_ctx.push()
            self._implicit_app_ctx_stack.append(app_ctx)
        else:
            self._implicit_app_ctx_stack.append(None)

这里我们就可以解释flask中wsgi_app这部分代码,每次web服务器为flask提供了http请求的environ变量,flask就会创建一个request_context对象,执行push()
之后上下文对象就会进入_request_ctx_stack中,并在执行处理函数之后会自动pop,
所以我们说请求上下文的生命周期就是一次请求的过程。

   def wsgi_app(self, environ, start_response):
        ctx = self.request_context(environ)
        error = None
        try:
            try:
                ctx.push()
                response = self.full_dispatch_request()
            except Exception as e:
                error = e
                response = self.handle_exception(e)
            except:
                error = sys.exc_info()[1]
                raise
            return response(environ, start_response)
        finally:
            if self.should_ignore_error(error):
                error = None
            ctx.auto_pop(error)

整体流程可参考:
https://www.jianshu.com/p/2a2...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/41133.html

相关文章

  • 从零开始搭建论坛(三):Flask框架简单介绍

    摘要:我们的论坛项目就使用了该框架。此外,麦子学院也有一个入门视频教程,一共小时的视频教程,涵盖开发的方方面面,包括环境的搭建,语法介绍,项目结构的组织,全球化,单元测试等内容。博客地址更多阅读的机制三个框架的对比 前面两篇文章中我们已经了解 Web(HTTP)服务器,Web应用程序,Web框架,WSGI这些 Python Web 开发中的概念。我们知道,Web框架通过将不同Web应用程序中...

    Alan 评论0 收藏0
  • 深入理解flask框架(3):configextension

    摘要:中的配置,都是通过来控制那么究竟是什么再来看函数而是什么再转向看的源码,可以看到类继承于字典也就是说的就是一个特殊的字典,用于保存配置项。 config flask中的配置,都是通过config来控制那么config究竟是什么? self.config = self.make_config(instance_relative_config) 再来看make_config函数: def ...

    wmui 评论0 收藏0
  • 深入 Flask 源码理解 Context

    摘要:主要的作用是将维护的字典中键为对应的值定义为。如果没有,则会将当前到中,同时将加入列表中否则添加。注意清理之后的动作。上述代码涉及到,它强调必须是一个可调用对象。后期的工作之一是了解。这仅仅是我的个人理解。实际上这是解决多个实例运行的问题。 Flask 中的上下文对象 知乎问题 编程中什么是「Context(上下文)」 已经能够简单地说明什么是 Context,它是一个程序需要的外部对...

    ninefive 评论0 收藏0
  • flask源码走读

    摘要:另外,如果你对模板渲染部分的内容感兴趣,也可以考虑阅读文档文档文档源码阅读,可以参考下面的函数打断点,再测试一个请求,理清过程。 Flask-Origin 源码版本 一直想好好理一下flask的实现,这个项目有Flask 0.1版本源码并加了注解,挺清晰明了的,我在其基础上完成了对Werkzeug的理解部分,大家如果想深入学习的话,可以参考werkzeug_flow.md. 阅读前 为...

    Coly 评论0 收藏0
  • 深入理解flask框架(4):session

    摘要:中的实现是基于。打开源码的文件,我们可以看到最后的接口类中,主要有两个函数。这个系列就此完结。 flask中session的实现是基于cookie。打开flask源码的session.py文件,我们可以看到最后的接口类中,主要有open_session,save_session两个函数。 class SecureCookieSessionInterface(SessionInterfa...

    PingCAP 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<