深刻理解flask框架(2):應用上下文與請求上下文

什麼是上下文?

flask框架中的上下文本質上就是兩個類,咱們能夠先看一下他的初始化函數:
應用上下文web

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

請求上下文flask

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 來保存多份狀態字典便可,就像按照門牌號隔開的一格一格的信箱。session

  • 實現一個 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類app

_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_添加一個應用上下文。ide

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...

相關文章
相關標籤/搜索