def wsgi_app(self, environ, start_response):
ctx = self.request_context(environ)
ctx.push()
error = None
try:
try:
response = self.full_dispatch_request()
except Exception as e:
error = e
response = self.make_response(self.handle_exception(e))
return response(environ, start_response)
finally:
if self.should_ignore_error(error):
error = None
ctx.auto_pop(error)
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()
#其它函數
# with語句協議
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)
在init函數我們可以看到有這些東西將被保存到RequestContext中:
- app
- request
- url_adapter
- flashes
- session
其中request = app.request_class(environ)中的request_class是Flask類的一個class variable. 默認情況下,request_class = Request, 在wrappers.py 我們查看Request類:
class Request(RequestBase):
url_rule = None
view_args = None
routing_exception = None
_is_old_module = False
@property
def max_content_length(self):
...
@property
def endpoint(self):
...
@property
def module(self):
...
@property
def blueprint(self):
...
@property
def json(self):
...
@property
def is_json(self):
...
def get_json(self, force=False, silent=False, cache=True):
...
def on_json_loading_failed(self, e):
...
Request繼承自werkzeug的Request類(示例)救湖,通過傳入environ進行初始化愧杯,獲得url_rule, view_args, endpoint等信息(示例)。
init函數最后會調用match_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
match_request函數配置了路由信息鞋既,可參考werkzeug路由示例力九。
下面我們做實驗看看request里面到底有些啥,
至此邑闺,ctx = self.request_context(environ) 執(zhí)行完畢跌前。ctx為一個RequestContext對象。
接下來ctx.psuh():
def push(self):
"""Binds the request context to the current context."""
# 這3行代碼跟調試異常有關陡舅,暫時忽略抵乓。
top = _request_ctx_stack.top
if top is not None and top.preserved:
top.pop(top._preserved_exc)
# 在push Request Context之前先保證push App 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 (e.g. code that access database information
# stored on `g` instead of the appcontext).
self.session = self.app.open_session(self.request)
if self.session is None:
self.session = self.app.make_null_session()
首先獲取app_ctx, app_ctx = self.app.app_context():
def app_context(self):
return AppContext(self)
class AppContext(object):
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
def push(self):
"""Binds the app context to the current context."""
self._refcnt += 1
if hasattr(sys, 'exc_clear'):
sys.exc_clear()
_app_ctx_stack.push(self)
appcontext_pushed.send(self.app)
與Request Context相比 AppContext含有的內容只有:
- app
- url_adapter
- g
相比RequestContext中的url_adapter,AppContext中提供給app.create_url_adapter的參數是None灾炭,那么AppContext只需要server name信息就足夠了茎芋。
def create_url_adapter(self, request):
if request is not None:
return self.url_map.bind_to_environ(request.environ,
server_name=self.config['SERVER_NAME'])
# We need at the very least the server name to be set for this
# to work.
if self.config['SERVER_NAME'] is not None:
return self.url_map.bind(
self.config['SERVER_NAME'],
script_name=self.config['APPLICATION_ROOT'] or '/',
url_scheme=self.config['PREFERRED_URL_SCHEME'])
對于g變量,app_ctx_globals_class = _AppCtxGlobals蜈出,即g變量是_AppCtxGlobals的一個實例:
class _AppCtxGlobals(object):
"""A plain object."""
def get(self, name, default=None):
return self.__dict__.get(name, default)
def pop(self, name, default=_sentinel):
if default is _sentinel:
return self.__dict__.pop(name)
else:
return self.__dict__.pop(name, default)
def setdefault(self, name, default=None):
return self.__dict__.setdefault(name, default)
def __contains__(self, item):
return item in self.__dict__
def __iter__(self):
return iter(self.__dict__)
def __repr__(self):
top = _app_ctx_stack.top
if top is not None:
return '<flask.g of %r>' % top.app.name
return object.__repr__(self)
換句話說田弥,g就是一個普通的對象,具體用法可參考flask.g.
app_ctx = self.app.app_context()執(zhí)行完畢铡原,執(zhí)行app_ctx.push()皱蹦,app context可以推送多次,但是一個基本的refcount足以來跟蹤它們眷蜈。_app_ctx_stack.push(self), 在globals.py中沪哺,我們看到:
# context locals
_request_ctx_stack = LocalStack()
_app_ctx_stack = LocalStack()
current_app = LocalProxy(_find_app)
request = LocalProxy(partial(_lookup_req_object, 'request'))
session = LocalProxy(partial(_lookup_req_object, 'session'))
g = LocalProxy(partial(_lookup_app_object, 'g'))
無論是_request_ctx_stack,還是_app_ctx_stack都是werkzeug中的LocalStack 實例,而current_app, request, session, g都是LocalProxy實例酌儒,關于LocalStack和LocalProxy的用法可參見另一篇文章辜妓。與AppContext相關的是_app_ctx_stack, current_app, g.
def _lookup_req_object(name):
top = _request_ctx_stack.top
if top is None:
raise RuntimeError(_request_ctx_err_msg)
return getattr(top, name)
def _lookup_app_object(name):
top = _app_ctx_stack.top
if top is None:
raise RuntimeError(_app_ctx_err_msg)
return getattr(top, name)
def _find_app():
top = _app_ctx_stack.top
if top is None:
raise RuntimeError(_app_ctx_err_msg)
return top.app
由于current_app是個代理對象,當我們對current_app進行操作時忌怎,實際上是對_find_app函數中返回的top.app進行操作的籍滴,即 _app_ctx_stack.top.app, 也即是AppContext().app(一個Flask類實例)進行操作。同樣 榴啸,對g進行操作孽惰,會轉交給getattr(_app_ctx_stack.top, 'g')也即是_AppCtxGlobals類的實例。至此AppContext push完畢鸥印。
回到push RequestContext過程中勋功,_request_ctx_stack.push(self),過程同上库说,request, session是與RequestContext相關的變量狂鞋。
因此,當我們寫from flask import current_app, request, session, g時潜的,這些變量看似是全局變量骚揍,實際上則是LocalProxy類實例。最后push()函數中還有幾行與session有關的代碼啰挪,暫時忽略信不。