您的位置:首页 > 其它

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

2017-12-29 12:01 1266 查看

什么是上下文?

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: