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

什么是上下文?

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/post/the-context-mechanism-of-flask/
- 为了实现线程之间的隔离
类似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类的本质是一个字典和一个获取到线程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]: {<greenlet.greenlet at 0x7f69d45b8f20>: {'stack': [<RequestContext 'http://localhost/' [GET] of __main__>]}}

In [9]: _app_ctx_stack._local.__storage__
Out[9]: {<greenlet.greenlet at 0x7f69d45b8f20>: {'stack': [<flask.ctx.AppContext at 0x7f69d4774cf8>]}}

我们可以看到一开始上下文均为空,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)

这里我们就可以解释wsgi_app这部分代码,每次web服务器提供了environ变量,就会创建一个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/2a2407f66438

猜你喜欢

转载自blog.csdn.net/yz764127031/article/details/78930077