Flask源码解读(2) -- context
来源:互联网 发布:ubuntu ftp传输文件 编辑:程序博客网 时间:2024/06/08 00:59
Flask源码解读(2) -- context
上篇我们讲到底层代码将请求打包成environ并传给app, app是一个定义了__call__方法的对象. __call__方法接收environ为参数并处理请求.
对于传入的__environ__, Flask内部将其转换并存储到context对象中. 视图函数可以通过context对象获取所有关于请求的信息, 另外context也提供了其他的功能
class Flask(_PackageBoundObject): 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) def __call__(self, environ, start_response): """Shortcut for :attr:`wsgi_app`.""" return self.wsgi_app(environ, start_response)从__call__方法中看出, 调用了wsgi_app方法. wsgi_app中首先调用request_context方法生成ctx
request_context方法用environ生成RequestContext对象
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 # Request contexts can be pushed multiple times and interleaved with # other request contexts. Now only if the last level is popped we # get rid of them. Additionally if an application context is missing # one is created implicitly so for each level we add this information self._implicit_app_ctx_stack = [] # indicator if the context was preserved. Next time another context # is pushed the preserved context is popped. self.preserved = False # remembers the exception for pop if there is one in case the context # preservation kicks in. self._preserved_exc = None # Functions that should be executed after the request on the response # object. These will be called before the regular "after_request" # functions. self._after_request_functions = [] self.match_request() def _get_g(self): return _app_ctx_stack.top.g def _set_g(self, value): _app_ctx_stack.top.g = value g = property(_get_g, _set_g) del _get_g, _set_g def copy(self): """Creates a copy of this request context with the same request object. This can be used to move a request context to a different greenlet. Because the actual request object is the same this cannot be used to move a request context to a different thread unless access to the request object is locked. .. versionadded:: 0.10 """ return self.__class__(self.app, environ=self.request.environ, request=self.request ) def match_request(self): """Can be overridden by a subclass to hook into the matching of the request. """ try: url_rule, self.request.view_args = \ self.url_adapter.match(return_rule=True) self.request.url_rule = url_rule except HTTPException as e: self.request.routing_exception = e def push(self): """Binds the request context to the current context.""" # If an exception occurs in debug mode or if context preservation is # activated under exception situations exactly one context stays # on the stack. The rationale is that you want to access that # information under debug situations. However if someone forgets to # pop that context again we want to make sure that on the next push # it's invalidated, otherwise we run at risk that something leaks # memory. This is usually only a problem in test suite since this # functionality is not active in production environments. top = _request_ctx_stack.top if top is not None and top.preserved: top.pop(top._preserved_exc) # Before we push the request context we have to ensure that there # is an application context. 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) if hasattr(sys, 'exc_clear'): sys.exc_clear() _request_ctx_stack.push(self) # Open the session at the moment that the request context is available. # This allows a custom open_session method to use the request context. # Only open a new session if this is the first time the request was # pushed, otherwise stream_with_context loses the session. if self.session is None: session_interface = self.app.session_interface self.session = session_interface.open_session( self.app, self.request ) if self.session is None: self.session = session_interface.make_null_session(self.app) def pop(self, exc=_sentinel): """Pops the request context and unbinds it by doing that. This will also trigger the execution of functions registered by the :meth:`~flask.Flask.teardown_request` decorator. .. versionchanged:: 0.9 Added the `exc` argument. """ app_ctx = self._implicit_app_ctx_stack.pop() try: clear_request = False if not self._implicit_app_ctx_stack: self.preserved = False self._preserved_exc = None if exc is _sentinel: exc = sys.exc_info()[1] self.app.do_teardown_request(exc) # If this interpreter supports clearing the exception information # we do that now. This will only go into effect on Python 2.x, # on 3.x it disappears automatically at the end of the exception # stack. if hasattr(sys, 'exc_clear'): sys.exc_clear() request_close = getattr(self.request, 'close', None) if request_close is not None: request_close() clear_request = True finally: rv = _request_ctx_stack.pop() # get rid of circular dependencies at the end of the request # so that we don't require the GC to be active. if clear_request: rv.request.environ['werkzeug.request'] = None # Get rid of the app as well if necessary. if app_ctx is not None: app_ctx.pop(exc) assert rv is self, 'Popped wrong request context. ' \ '(%r instead of %r)' % (rv, self) def auto_pop(self, exc): if self.request.environ.get('flask._preserve_context') or \ (exc is not None and self.app.preserve_context_on_exception): self.preserved = True self._preserved_exc = exc else: self.pop(exc) def __enter__(self): self.push() return self def __exit__(self, exc_type, exc_value, tb): # do not pop the request stack if we are in debug mode and an # exception happened. This will allow the debugger to still # access the request object in the interactive shell. Furthermore # the context can be force kept alive for the test client. # See flask.testing for how this works. self.auto_pop(exc_value) if BROKEN_PYPY_CTXMGR_EXIT and exc_type is not None: reraise(exc_type, exc_value, tb) def __repr__(self): return '<%s \'%s\' [%s] of %s>' % ( self.__class__.__name__, self.request.url, self.request.method, self.app.name, )
上面就是RequestContext类的完整定义, 在Flask的wsgi_app方法中可以看到, 每个请求开始时, 新建request_context对象并调用request_context.push(), 在请求处理完后调用request_context.auto_pop()
在request_context的__init__方法中, 我们看到了经常使用的self.request, self.url_adapter, self.flashes, self.session属性. url_adapter和session后续讲解
另外通过property, 也可以访问self.g属性. self.g实际指向app_context的g属性.
重点看看request_context对象的push方法
push方法就是将request_context推入到_request_ctx_stack中. 当一个请求处理过程中出现异常或者用户指定保留request_context时, 在请求的结束时, request_context不会弹出栈
这样就有可能出现在push时发现栈中已经存在某个request_context. 因此在push时, 首先判断并删除栈中已经存在的request_context
紧接着通过app_ctx = self.app.app_context(), app_ctx.push()两条语句, 可以发现在request_context入栈时, 会自动新建一个app_context并将其入栈.
然后就是关于self.session的初始化操作, 我们后文介绍
接着看看request_context对象的auto_pop方法
判定条件 self.request.environ.get('flask._preserve_context') or (exc is not None and self.app.preserve_context_on_exception): 正是说明如果出现异常, 或者用户指定, 将只执行
self.preserved = True, self._preserved_exc = exc两条语句, 而不会执行pop操作. 执行的两条语句将当前request_context标记, 保证下次有request_context入栈时首先被弹出
如果正常情况下, auto_pop会调用pop. 在pop中, 首先通过_implicit_app_ctx_stack找到了建立request_context时, 一同建立的app_context, 将此app_context出栈, 再自己出栈.
总结:
每次处理一个请求前, 会建立context
context包括request_context, app_context, 一般request_context出入栈的时候, 会先将app_context出入栈
- Flask源码解读(2) -- context
- 解读flask框架,flask源码解读
- Flask源码解读(3) -- route
- Flask源码解读 <2> --- 请求上下文和request对象
- Flask源码解读 <1> --- 浅谈Flask基本工作流程
- 从源码解读context对象的作用
- flask-cache 缓存Jinja2模板之源码解读
- Flask源码解读(1) -- app.run()的背后
- Flask 的 Context 机制
- Flask的Context(上下文)
- CppUnit源码解读(2)
- CYYMysql 源码解读 2
- jqzoom源码解读 2
- mybatis源码解读(2)
- spring-session源码解读-2
- vueJs源码解读0-2
- JUnit 4.8 源码解读2
- faster rcnn源码解读2
- Ajax的使用
- Android.mk介绍
- 第四周项目3
- c#第一天
- kubernetes之存储学习整理
- Flask源码解读(2) -- context
- 图像金字塔
- JS——for...of...
- Android.mk的用法和基础
- 游戏留言区(六)
- android ANR发生的原因总结和解决办法
- 游戏留言区(七)
- 二分查询
- 游戏留言区(二)