一、flask源碼分析
# 創建一個flask項目
from flask import Flask
app = Flask(__name__)
if __name__ == '__main__':
# app.__call__()
app.run()
1.查看app.run()中run方法
首先進入app.run()方法,進入之后你會發現最核心的一句話, 導入了werkzeug工具包,啟動了一個socket,其中self是app有Flask類創建的對象,host是ip地址,port是端口號
from werkzeug.serving import run_simple
# app.run調用werkzeug.seriving的run-simple
run_simple(host, port, self, **options)
2.查看__call__方法
在進入Flask類中的__call__,發現的是里面有一個是wssgi_app方法將執行的結果返回回來,其中參數意思為:
- self是app由flask創建的實例對象
- environ:請求相關的參數也就是前端頁面向后端發送請求攜帶過來的所有參數信息
- start_response: 是請求響應相關的(response)
def __call__(self, environ, start_response):
"""The WSGI server calls the Flask application object as the
WSGI application. This calls :meth:`wsgi_app` which can be
wrapped to applying middleware."""
return self.wsgi_app(environ, start_response)
3.查看wsgi_app源碼
3.1總體代碼分析
- self:flask創建對象
- environ:是請求相關
- start_response:
def wsgi_app(self, environ, start_response):
# 獲取請求相關,存到ctx,得到一個Rquest_context對象,這對象里面包含了requests和session
ctx = self.request_context(environ) # 見3.2
error = None
try:
try:
# 把ctx存起來 見3.3
ctx.push()
# 執行任務獲取response
# 從ctx中獲取請求的東西,是所有響應的東西
response = self.full_dispatch_request()
except Exception as e:
error = e
# 捕獲異常
response = self.handle_exception(e)
except: # noqa: B001
error = sys.exc_info()[1]
raise
return response(environ, start_response)
finally:
if self.should_ignore_error(error):
error = None
# 刪除ctx,請求結束后執行ctx.auto_pop(error)把它在LOCAL對象中刪除.刪除的是ctx
ctx.auto_pop(error)
3.2ctx代碼分析
ctx = self.request_context(environ)
分析,environ是請求相關request
1.進入request_content方法中,會返回一個RequestContext(self, environ)對象,self是app是flask創建的對象,environ是請求相關的數據,如下:
def request_context(self, environ):
return RequestContext(self, environ)
2.進入RequestContext類,在__init__中出現創建了將請求相關的所有數據都賦值給了request,並設置了session
def __init__(self, app, environ, request=None, session=None):
self.app = app
if request is None:
request = app.request_class(environ)
self.request = request
self.url_adapter = None
try:
self.url_adapter = app.create_url_adapter(self.request)
except HTTPException as e:
self.request.routing_exception = e
self.flashes = None
self.session = session
3.總結:ctx = self.request_context(environ)
,ctx是一個RequestContext類對象,返回來這個對象中包含了request,session還有其他的屬性
3.3 ctx.push() 把ctx保存起來
# globals.py類中實現的封裝,只要項目已啟動就會創建
_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"))
1.ctx.push()方法
# self是ctx
def push(self):
top = _request_ctx_stack.top # return self._local.stack[-1] 不能理解,top是一個方法屬性,返回最后一個請求
if top is not None and top.preserved:
top.pop(top._preserved_exc)
.....
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)
pass
# local.py中push,將ctv添加進去
def push(self, obj):
"""Pushes a new item to the stack"""
rv = getattr(self._local, "stack", None)
if rv is None:
self._local.stack = rv = []
rv.append(obj)
return rv
2.在push中調用了_request_ctx_stack.push(self)
方法,self是ctx,使用該方法需要導入from .globals import _request_ctx_stack
說明:_request_ctx_stack
這個是一個全局變量是LocalStack()的對象
進入globals.py中會有如下:
# globals.py類中實現的封裝,只要項目已啟動就會創建
_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"))
最終調用是LocalStack()對象在localStack的初始化方法中生成的是 self._local = Local()
Local類對象,
def __init__(self):
self._local = Local()
Local類對象的初始化方法如下:
def __init__(self):
object.__setattr__(self, "__storage__", {})
object.__setattr__(self, "__ident_func__", get_ident)
3.最終調用的是LocalStack()類中的push方法內容如下,
說明:_request_ctx_stack.push(self)self是LocalStack產生的對象,obj是ctv:
"""
1. self是LocalStack()產生的對象
2. self._local: 他是Local()類對象,從Local獲取stack對象其實也就是
3. obj是ctv
4. 在push才將ctv進行保存了起來 rv.append(obj)
"""
def push(self, obj):
rv = getattr(self._local, "stack", None) # 從local中獲取stack如果沒有獲取到則會將ctv進行添加進來
if rv is None:
self._local.stack = rv = []
# 把obj,也就是ctx存到了LOCAL對象中的storage,存的格式為: storage={"執行id(線程id)":{'stack':[ctx,]}}
rv.append(obj)
return rv
總結:真正保存的是在_request_ctx_stack.push(self)
中完成了ctv的保存
3.4 response = self.full_dispatch_request()分析
response = self.full_dispatch_request(self)
獲取所有響應的東西,self是app是通過flask創建的對象
# 這個里面所有self都是app
def full_dispatch_request(self):
# 執行第一次請求需要執行的所有函數
# 見3.4.1說明
self.try_trigger_before_first_request_functions()
try:
# 信號
request_started.send(self)
# rv 就是響應函數嗎?
# 見3.4.2說說明
#rv是 before_request執行的結果,見3.4.2說說明,如果這個里面有返回值,后面的befor_request都不會執行
rv = self.preprocess_request()
# rv如果為None,才會執行下面self.dispatch_request()才是真正的響應函數
if rv is None:
# rv 真正的響應函數,如果上面的before_request沒有返回值就執行它,並拿到rv這個返回值,去獲取對應路由返回的結果
# 見3.4.3說明
rv = self.dispatch_request() # 獲取url請求響應結果
except Exception as e:
rv = self.handle_user_exception(e)
# 請求之后的函數response,獲取響應對象 見3.4.4說明
return self.finalize_request(rv)
3.4.1 self.try_trigger_before_first_request_functions()
self.try_trigger_before_first_request_functions():說明在請求來的時候都會被調用並且只會調用一次, 這個方法自己可以用裝飾器@app.before_first_request
修飾的函數都會被執行,只會執行一次,具體實現執行查看try_trigger_before_first_request_functions
方法
# self是app
def try_trigger_before_first_request_functions(self):
if self._got_first_request:
return
with self._before_request_lock:
if self._got_first_request:
return
for func in self.before_first_request_funcs:
func()
self._got_first_request = True
@setupmethod
def before_first_request(self, f):
# 添加被`@app.before_first_request`裝飾的第一次請求的函數
self.before_first_request_funcs.append(f)
return f
3.4.2 self.preprocess_request()
執行被@befor_request
裝飾的請求函數是請求相關,其中只有有一個請求響應中函數中包含了return下面的請求函數都將不會被執行,如果有則將rv返回,如果不存在則返回一個None,系統的或者是自己通過裝飾器創建請求相關的
# self是app
def preprocess_request(self):
bp = _request_ctx_stack.top.request.blueprint
funcs = self.url_value_preprocessors.get(None, ())
if bp is not None and bp in self.url_value_preprocessors:
funcs = chain(funcs, self.url_value_preprocessors[bp])
for func in funcs:
func(request.endpoint, request.view_args)
funcs = self.before_request_funcs.get(None, ())
if bp is not None and bp in self.before_request_funcs:
funcs = chain(funcs, self.before_request_funcs[bp])
for func in funcs:
# before_request被裝飾器裝飾的請求函數的執行結果,重要
rv = func()
if rv is not None:
return rv
3.4.3 self.preprocess_request()
full_dispatch_request
中的rv = self.dispatch_request()真正的請求響應,這個函數所要做的事情就是通過url請求了鏈接中對應的請求去獲取響應結結果,就是通過路由去獲取函數的結果
# self是app
def dispatch_request(self):
req = _request_ctx_stack.top.request # top是ctv
if req.routing_exception is not None:
self.raise_routing_exception(req)
# 請求url路徑
rule = req.url_rule
# if we provide automatic options for this URL and the
# request came with the OPTIONS method, reply automatically
if (
getattr(rule, "provide_automatic_options", False)
and req.method == "OPTIONS"
):
# 獲取真正的請求響用
return self.make_default_options_response() # 返回的是一個Response對象,響用結果
# otherwise dispatch to the handler for that endpoint
return self.view_functions[rule.endpoint](**req.view_args)
3.4.4 self.finalize_request(rv)請求之后的函數
獲取響應結果response
# self是app
def finalize_request(self, rv, from_error_handler=False):
# rv可能是字符串,可以是html,json數據
# 創建respon返回對象
response = self.make_response(rv)
try:
# 去執行被@app.after_request裝飾的app響應結果 見3.4.4.1說明
response = self.process_response(response)
request_finished.send(self, response=response)
except Exception:
if not from_error_handler:
raise
self.logger.exception(
"Request finalizing failed with an error while handling an error"
)
# 返回響應結果
return response
3.4.4.1 self.process_response(response)
遍歷查詢當前app中的被@app.after_request裝飾的函數
# 去執行自己創建的被@app.after_request裝飾的函數結果
def process_response(self, response):
ctx = _request_ctx_stack.top
bp = ctx.request.blueprint
funcs = ctx._after_request_functions
if bp is not None and bp in self.after_request_funcs:
funcs = chain(funcs, reversed(self.after_request_funcs[bp]))
if None in self.after_request_funcs:
funcs = chain(funcs, reversed(self.after_request_funcs[None]))
for handler in funcs:
response = handler(response)
if not self.session_interface.is_null_session(ctx.session):
self.session_interface.save_session(self, ctx.session, response)
return response
4.request對象
@app.route("/")
def index():
print(request.method)
return "ok"
from flask import request
# ctrl+鼠標左鍵進入,會找到如下結果
# partial(_lookup_req_object, "request") 這句話意思是執行_lookup_req_object方法,從 _request_ctx_stack.top 也就是ctv中獲取 request屬性將其返回
request = LocalProxy(partial(_lookup_req_object, "request")) localproxy代理
# LocalProxy():中的初始化方法,localrequest
def __init__(self, local, name=None):
object.__setattr__(self, "_LocalProxy__local", local)
object.__setattr__(self, "__name__", name)
if callable(local) and not hasattr(local, "__release_local__"):
# "local" is a callable that is not an instance of Local or
# LocalManager: mark it as a wrapped function.
object.__setattr__(self, "__wrapped__", local)
object.setattr(self, "_LocalProxy__local", local)解釋:因為在LocalSack類的初始化方法中出現設置了隱藏屬性self._local = Local()
,所有可以通過_LocalProxy__local獲取local,當我們通過requst.args獲取參數的時候會調用該類中的__getattr__通過反射(request)中獲取args(.method)具體屬性如下:
# name就是args, host,url你要獲取屬性值的名字,name是request中的.method屬性
# 從self._get_current_object()找具體的屬性結果
def __getattr__(self, name):
if name == "__members__":
return dir(self._get_current_object())
# self._get_current_object()返回的結果就是偏函數執行的結果,偏函數返回的結果就是request:見下面
# 從request中獲取屬性值
return getattr(self._get_current_object(), name)
# local,LocalProxy(partial(_lookup_req_object, "request")) 傳過來的是ctv,local也就是偏函數,
# 從_LocalProxy__local,其實是LocalProxy中的隱藏屬性
def __init__(self, local, name=None):
object.__setattr__(self, "_LocalProxy__local", local)
object.__setattr__(self, "__name__", name)
if callable(local) and not hasattr(local, "__release_local__"):
# "local" is a callable that is not an instance of Local or
# LocalManager: mark it as a wrapped function.
object.__setattr__(self, "__wrapped__", local)
#sdf
def _get_current_object(self):
"""Return the current object. This is useful if you want the real
object behind the proxy at a time for performance reasons or because
you want to pass the object into a different context.
"""
if not hasattr(self.__local, "__release_local__"):
# self.__local也就是partial(_lookup_req_object, "request")偏導函數執行的結果
# self.__local()加括號執行也就是偏導函數加括號運行
return self.__local()
try:
return getattr(self.__local, self.__name__)
except AttributeError:
raise RuntimeError("no object bound to %s" % self.__name__)