Flask werkzeug 源碼解析

Flask werkzeug流程大概:執行run_simple ,實際執行為先用make_server 創建一個 BaseServer 實例,然后執行?實例的serve_forever 方法,?serve_forever 調用??run_simple 傳入的第三個參數,執行(self, environ, start_response) ,environ 為 初步處理的request 請求,start_response 為回調函數;

若第三個參數為? ?Response 的實例化對象,以下稱obj,obj 是具體的request 入口,負責具體的邏輯,其他不同的框架其實是第三個參數不同,Flask由此而來。Flask 的三種方式

from werkzeug.wrappers import Request, Response
from werkzeug.serving import run_simple

# 方式一:實例化Response,最后 self.request = encode('Hello World application1!')
application1 = Response('Hello World application1!')


#方式二
def application2(environ, start_response):
? ? request = Request(environ)
? ? response = Response("Hello %s!" % request.args.get('name', 'World!'))
? ? return response(environ, start_response)


#方式三
@Request.application
def hello(request):
? ? return Response('Hello World Request!')

if __name__ == '__main__':
? ? # run_simple('localhost', 4000, application1)
? ? # run_simple('localhost', 4000, application2)
? ? run_simple('localhost', 4000, hello)

我們在瀏覽器輸入http://localhost:4000/就會得到response信息?
接下來我們就簡單的分析下,該模塊的請求、響應流程

werkzeug包下的__init__.py模塊初始化時,遍歷循環字典all_by_module,重新構造object_origins字典數據格式,該字典類型格式如下,我列舉出來一些元素,以下是鍵值對形式

# BaseResponse - --- werkzeug.wrappers
# BaseRequest - --- werkzeug.wrappers
# Request - --- werkzeug.wrappers
# Response - --- werkzeug.wrappers

該字典的鍵是werkzeug下的某模塊中的函數、方法,值是werkzeug下的某模塊中。我們回頭看我們的demo示例,在文件起始處我們引入了from werkzeug.serving import run_simple。我們跟蹤代碼去看下serving.py模塊下的run_simple函數

####################### serving.py 文件下的 run_simple 函數 ############################
#??run_simple最后執行inner函數
def run_simple(hostname, port, application, use_reloader=False,use_debugger=False, use_evalex=True,
? ? extra_files=None, reloader_interval=1,reloader_type='auto', threaded=False,
? ? processes=1, request_handler=None, static_files=None,passthrough_errors=False, ssl_context=None)

# 參數
hostname:應用程序的主機
port:端口
application:WSGI應用程序
use_reloader:如果程序代碼修改,是否需要自動啟動服務
use_debugger:程序是否要使用工具和調試系統
use_evalex:應用是否開啟異常評估
extra_files:重載器應該查看的文件列表附加到模塊。例如配置文件夾
reloader_interval:秒重載器的間隔
reloader_type:重載器的類型
threaded:進程是否處理單線程的每次請求
processes:如果大于1,則在新進程中處理每個請求。達到這個最大并發進程數
request_handler:可以自定義替換BaseHTTPRequestHandler
static_files:靜態文件路徑的列表或DICT
passthrough_errors:將此設置為“真”以禁用錯誤捕獲。這意味著服務器會因錯誤而死亡
ssl_context:如何進行傳輸數據加密,可以設置的環境


# run_simple函數中,最后會執行到
if use_reloader:
? ? # 省略,太長了,反正暫時也執行不到
else:
? ? inner()

################ serving.py 文件下的 run_simple 函數下的 inner()函數 #####################
def inner():
? ? try:
? ? ? ? fd = int(os.environ['WERKZEUG_SERVER_FD'])
? ? except (LookupError, ValueError):
? ? ? ? fd = None
? ? # 通過make_server方法,跟進我們在初始化__init__中的參數,去構造server服務
? ? srv = make_server(hostname, port, application, threaded,processes,?request_handler,passthrough_errors, ssl_context,fd=fd)
? ? if fd is None:
? ? ? ? log_startup(srv.socket)
? ? # 把服務運行起來 serve_forever() 是HTTPserver的方法,
? ? # 當有請求過來之后,server_forever會將run_simple()中的第三個參數加括號執行,并傳入參數(environ, start_response)

? ? srv.serve_forever()
############### BaseWSGIServer 類下的 serve_forever 方法 #######################
class BaseWSGIServer(HTTPServer, object)
? ? def serve_forever(self):
? ? ? ? self.shutdown_signal = False
? ? ? ? try:
? ? ? ? ? ? # class HTTPServer(socketserver.TCPServer)未實現 serve_forever ————>?
? ? ? ? ? ? # class TCPServer(BaseServer) 未實現 serve_forever :
? ? ? ? ? ? # BaseServer 實現 serve_forever
? ? ? ? ? ? HTTPServer.serve_forever(self)
? ? ? ? except KeyboardInterrupt:
? ? ? ? ? ? pass
? ? ? ? finally:
? ? ? ? ? ? self.server_close()
################ serving.py 文件下的 def make_server ?#####################
def make_server(host=None, port=None, app=None, threaded=False, processes=1,request_handler=None,
? ? ? ? ? ? ? ? passthrough_errors=False,ssl_context=None, fd=None):

? ? """Create a new server instance that is either threaded, or forks
? ? or just processes one request after another."""

? ? if threaded and processes > 1:
? ? ? ? raise ValueError("cannot have a multithreaded and multi process server.")
? ? elif threaded:
? ? ? ? return ThreadedWSGIServer(host, port, app, request_handler,passthrough_errors, ssl_context, fd=fd)
? ? elif processes > 1:
? ? ? ? return ForkingWSGIServer(host, port, app, processes, request_handler,passthrough_errors, ssl_context, fd=fd)
? ? else:
? ? ? ? # 沒想到吧,最后執行這個 ,實例化BaseWSGIServer
? ? ? ? return BaseWSGIServer(host, port, app, request_handler,passthrough_errors, ssl_context, fd=fd)
########## socketserver.py 文件下的 class BaseServer 下的 serve_forever #######
# 具體的監聽 socket ,當有請求到來時,執行傳入的第三個 參數,執行格式為 函數(self, environ, start_response),并接受返回值
class BaseServer:
? ? def serve_forever(self, poll_interval=0.5):

? ? ? ? """Handle one request at a time until shutdown.
? ??
? ? ? ? Polls for shutdown every poll_interval seconds. Ignores
? ? ? ? self.timeout. If you need to do periodic tasks, do them in
? ? ? ? another thread.
? ? ? ? """
? ? ? ? self.__is_shut_down.clear()
? ? ? ? try:
? ? ? ? ? ? # XXX: Consider using another file descriptor or connecting to the
? ? ? ? ? ? # socket to wake this up instead of polling. Polling reduces our
? ? ? ? ? ? # responsiveness to a shutdown request and wastes cpu at all other
? ? ? ? ? ? # times.
? ? ? ? ? ? with _ServerSelector() as selector:? ?# 具體監聽過程
? ? ? ? ? ? ? ? selector.register(self, selectors.EVENT_READ)

? ? ? ? ? ? ? ? while not self.__shutdown_request:?
? ? ? ? ? ? ? ? ? ? ready = selector.select(poll_interval)
? ? ? ? ? ? ? ? ? ? if ready:
? ? ? ? ? ? ? ? ? ? ? ? self._handle_request_noblock()

? ? ? ? ? ? ? ? ? ? self.service_actions()
? ? ? ? finally:
? ? ? ? ? ? self.__shutdown_request = False
? ? ? ? ? ? self.__is_shut_down.set()

我們看下我們的示例中,最簡單那個例子application1 = Response('Hello World application1!'),設置run_simple('localhost', 4000, application1),當接受請求時,為什么會執行application1的對象內方法,并且返回給瀏覽器

在我們示例代碼中,當run_simple('localhost', 4000, application1)執行后,實際執行inner(),而inner做了兩件事,一件make_server,一件server_forver(一直在監聽)。當有請求過來之后,server_forever會將run_simple()中的第三個參數加括號執行,并傳入參數(environ, start_response)。所以當http://localhost:4000/請求時,就會觸發并調用application1(),即application1 = Response('Hello World application1!').__call(self, environ, start_response)__

在所有的 python web 框架都要遵循 WSGI 協議,WSGI 中有一個非常重要的概念:

每個 python web 應用都是一個可調用(callable)的對象(如上述的Response),要運行 web 應用,必須有 web server,在werkzeug中提供了 WSGIServer,Server 和 Application 之間怎么通信,就是 WSGI 的功能

wsgi有兩方,服務器方 和 應用程序

  1. 服務器方:調用應用程序,給應用程序傳遞(環境信息)和(回調函數), 這個回調函數是用來將應用程序設置的http header和status等信息傳遞給服務器方.
  2. 應用程序:用來生成返回的header,body和status,以便返回給服務器方。

看完了請求,接下來看下返回。即werkzeug.wrappers.py模塊下的Response類

################### response.py 文件下的 class Response ################
class Response(BaseResponse, ETagResponseMixin, ResponseStreamMixin,CommonResponseDescriptorsMixin,WWWAuthenticateMixin):
# 就這么多,沒了,氣不氣

該類是多重繼承類,這里主要看下BaseResponse,先看下初始方法

#################### base_responce.py 文件下的 class BaseResponse(object) #############
class BaseResponse(object)def __init__(self, response=None, status=None, headers=None,mimetype=None, content_type=None, direct_passthrough=False):if isinstance(headers, Headers):self.headers = headerselif not headers:self.headers = Headers()else:self.headers = Headers(headers)if content_type is None:if mimetype is None and 'content-type' not in self.headers:mimetype = self.default_mimetypeif mimetype is not None:mimetype = get_content_type(mimetype, self.charset)content_type = mimetypeif content_type is not None:self.headers['Content-Type'] = content_typeif status is None:status = self.default_statusif isinstance(status, integer_types):self.status_code = statuselse:self.status = statusself.direct_passthrough = direct_passthroughself._on_close = []# we set the response after the headers so that if a class changes# the charset attribute, the data is set in the correct charset.if response is None:self.response = []elif isinstance(response, (text_type, bytes, bytearray)):self.set_data(response)   #################看這里else:self.response = response

在BaseResponse類__init__初始方法中,我們定義了返回的Headers、content_type、狀態碼status,最后通過self.set_data(response),跟蹤代碼如下:

####### base_responce.py 文件下的 class BaseResponse(object) 下的 def set_data ##########
## 主要講傳入的應答 編碼
class BaseResponse(object)def set_data(self, value):if isinstance(value, text_type):# 字符串編碼value = value.encode(self.charset)else:value = bytes(value)self.response = [value]  # 看這里,看這里if self.automatically_set_content_length:self.headers['Content-Length'] = str(len(value))

將我們示例中的application1 = Response('Hello World application1!')參數字符串,進行bytes類型轉換并賦值給self.response,然后執行對象(),即調用__call__方法,

####### base_responce.py 文件下的 class BaseResponse(object) 下的 def __call__ ##########
# 這個方法的作用就是,執行 具體的請求過程,然后調用回調函數,并提供返回值給調用者 HTTPServer.serve_forever(self)
class BaseResponse(object)def __call__(self, environ, start_response):print(start_response)# get_wsgi_response ,是具體的請求處理過程,后面Flask源碼解析會講到app_iter, status, headers = self.get_wsgi_response(environ)# start_response ,提供的回調函數start_response(status, headers)return app_iter  ### 把值返回個調用者

這里要先介紹一個environ參數,以上方式2中涉及到了environ,其實這個environ參數是包含了請求的所有信息,讓我們在看下__call__方法中, app_iter, status, headers = self.get_wsgi_response(environ)輸出通過請求系列參數,獲取最后要返回的get_wsgi_response,輸出如下:

<werkzeug.wsgi.ClosingIterator object at 0x0589C0B0> --- 200 OK --- [('Content-Type'\\\省略]

然后在start_response(status, headers)代碼中,start_response 是 application 處理完之后需要調用的函數,參數是狀態碼、響應頭部還有錯誤信息,讓我們來看下start_response輸出,

<function WSGIRequestHandler.run_wsgi.<locals>.start_response at 0x05A32108>

跟蹤代碼如下start_response:

def start_response(status, response_headers, exc_info=None):if exc_info:try:if headers_sent:reraise(*exc_info)finally:exc_info = Noneelif headers_set:raise AssertionError('Headers already set')headers_set[:] = [status, response_headers]return write

start_response返回write方法,然后跟蹤該方法

def write(data):assert headers_set, 'write() before start_response'if not headers_sent:status, response_headers = headers_sent[:] = headers_settry:code, msg = status.split(None, 1)except ValueError:code, msg = status, ""code = int(code)self.send_response(code, msg)header_keys = set()for key, value in response_headers:self.send_header(key, value)key = key.lower()header_keys.add(key)if not ('content-length' in header_keys or environ['REQUEST_METHOD'] == 'HEAD' or code < 200 or code in (204, 304)):self.close_connection = Trueself.send_header('Connection', 'close')if 'server' not in header_keys:self.send_header('Server', self.version_string())if 'date' not in header_keys:self.send_header('Date', self.date_time_string())self.end_headers()assert isinstance(data, bytes), 'applications must write bytes'self.wfile.write(data)self.wfile.flush()

最后就輸出到瀏覽器,以上就是簡單的請求、響應流程

Flask示例

我們在Flask中經常會寫成

from flask import Flaskapp = Flask(__name__)@app.route('/index')
def index():return 'Hello World'if __name__ == '__main__':app.run() # run_simple(host,port,app)

看一下run?

def run(self, host=None, port=None, debug=None, **options):from werkzeug.serving import run_simpleif host is None:host = '127.0.0.1'if port is None:server_name = self.config['SERVER_NAME']if server_name and ':' in server_name:port = int(server_name.rsplit(':', 1)[1])else:port = 5000if debug is not None:self.debug = bool(debug)options.setdefault('use_reloader', self.debug)options.setdefault('use_debugger', self.debug)try:run_simple(host, port, self, **options)  ## 是不是還是用的run_simplefinally:self._got_first_request = False

最后依然是執行的run_simple(host, port, self, **options),也就是werkzeug.serving.py下的run_simple方法

####################### Flask 的 __call__ 函數 ########################
def __call__(self, environ, start_response):"""The WSGI server calls the Flask application object as theWSGI application. This calls :meth:`wsgi_app` which can bewrapped to applying middleware."""return self.wsgi_app(environ, start_response)

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/454475.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/454475.shtml
英文地址,請注明出處:http://en.pswp.cn/news/454475.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

AVS 幀內預測模式的匯編優化

王瑞&#xff0a;基金項目&#xff1a;本課題得到國家自然科學基金資助項目基金&#xff08;項目編號&#xff1a;60772101&#xff09;的資助。作者簡介&#xff1a;王瑞&#xff08;1986—&#xff09;, 男, 山東萊蕪人, 碩士, 主要從事視頻壓縮方面的研究. E&#xff0d;mai…

ltsc系統激活_WIN10_X64企業版LTSC 電腦公司裝機版 202008

文件: WIN10_X64_LTSC_ZJ202008.esd大小: 7431429353 字節(6.92G)MD5: A3A3B15ED47216E177C924D2E07E0799SHA1: 3A647265E0C8234225C633407093BAA07253FB34CRC32: 32E791E9(注意&#xff0c;下載文件有一定幾率損壞&#xff0c;如文件值不對請重新下載&#xff01;)360安全云盤…

大學計算機應用基礎考試題庫,大學計算機應用基礎考試題庫

綜合模擬(四)一、選擇題。1、完整的計算機硬件系統一般包括外部設備和 C 。A、運算器的控制器 B、存儲器 C、主機 D、中央處理器2、計算機能夠自動工作&#xff0c;主要是因為采用了 D 。A、二進制數制 B、高速電子元件 C、存儲程序控制 D、程序設計語言3、下面哪能一組是系統軟…

Lombok 使用小結

Lombok 使用小結 Lombok 簡介Lombok 安裝Lombok 使用 API示例示例源碼引用和引申Lombok 簡介 Lombok 是一種 Java 實用工具&#xff0c;可用來幫助開發人員消除 Java 的冗長&#xff0c;尤其是對于簡單的 Java 對象&#xff08;POJO&#xff09;。它通過注釋實現這一目的。通過…

html表單input file,input標簽type=file的文件上傳

一&#xff0c;通過表單提交的方式該提交方式只是提交普通表單&#xff1b;對于file組所選中的文件內容是不上傳的&#xff0c;因此需要設置&#xff1a;enctype屬性enctype"multipart/form-data"如果想上傳多文件&#xff0c;可添加multiple二&#xff0c;通過Ajax異…

AVS游程解碼、反掃描、反量化和反變換優化設計

中圖分類號:TN919.81   文獻標識碼:A   文章編號:1009-2552 (2007) 02-0054-04AVS游程解碼、反掃描、反量化和反變換優化設計趙 策, 劉佩林(上海交通大學電子工程系, 上海200240)摘 要: 提出了一種適用于AVS的游程解碼、反掃描、反量化和反變換硬件結構優化設計方案。根據…

Django REST framework介紹

現在前后端分離的架構設計越來越流行&#xff0c;業界甚至出現了API優先的趨勢。 顯然API開發已經成為后端程序員的必備技能了&#xff0c;那作為Python程序員特別是把Django作為自己主要的開發框架的程序員&#xff0c;Django REST framework&#xff08;DRF&#xff09;這個…

zabbix 安裝_安裝zabbix

準備一個純凈環境10.0.0.99首先修改yum源&#xff0c;修改為zabbix清華源&#xff0c;清華源玉zabbix官方源都是同步的&#xff0c;下載速度更快&#xff01;zabbix官方Download Zabbix?www.zabbix.com點擊下載&#xff0c;下面有zabbix的歷史版本以及官方安裝文檔可以查看到不…

拓展歐幾里得 [Noi2002]Savage

對于一個野人&#xff0c;他&#xff08;她&#xff1f;&#xff09;所在的位置&#xff0c;&#xff08;C[i]x*p[i]&#xff09;%ans,是的&#xff0c;暴力枚舉每一個ans&#xff0c;用拓展歐幾里得求出每兩個wildpeople(wildrage?)相遇的年份&#xff0c;如果小于最小的壽限…

CCNP-19 IS-IS試驗2(BSCI)

CCNP-19 IS-IS試驗2 實驗拓撲&#xff1a;試驗要求&#xff1a;R1 R2 R3全部采用集成的ISIS路由協議&#xff0c;R1 R2在區域49.0001內&#xff0c;R3在區域49.0002內&#xff0c;R1與R2之間的鏈路類型為L1&#xff0c;R2與R3之間的鏈路類型為L2。 試驗目的&#xff1a;掌握基…

正道的光用計算機,正道的光作文500字

當那熟悉的轟天巨雷般的呼嚕聲響起&#xff0c;我就知道&#xff0c;這又是睡不著的一天。同樣在宿舍&#xff1b;同樣是小翟&#xff1b;同樣的時間&#xff1b;同樣在我昏昏欲睡的時候&#xff0c;那個熟悉的呼嚕聲&#xff0c;它又來了。它將我從即將到來的美夢中驚醒了&…

AVS高清立體視頻編碼器

一、成果項目背景 電視技術在經歷了從黑白到彩色、從模擬到數字的技術變革之后正在醞釀另一場技術革命&#xff0c;從單純觀看二維場景的平面電視跨越到展現三維場景的立體電視。立體電視&#xff0c;又稱三維電視(3DTV)&#xff0c;提供了更為豐富的視覺信息和更具臨場感的觀…

RESTful介紹

RESTful介紹 REST與技術無關&#xff0c;代表的是一種軟件架構風格&#xff0c;REST是Representational State Transfer的簡稱&#xff0c;中文翻譯為“表征狀態轉移”或“表現層狀態轉化”。阮一峰 理解RESTful架構 RESTful API設計指南 阮一峰 RESTful設計指南 API與用戶…

dijkstra算法代碼_數據科學家需要知道的5種圖算法(附代碼)

在本文中&#xff0c;我將討論一些你應該知道的最重要的圖算法&#xff0c;以及如何使用Python實現它們。作者&#xff1a;AI公園導讀因為圖分析是數據科學家的未來。作為數據科學家&#xff0c;我們對pandas、SQL或任何其他關系數據庫非常熟悉。我們習慣于將用戶的屬性以列的形…

大暴搜 chess

仔細讀題&#xff0c;會發現吃掉敵人點對方案數的貢獻很神奇。如果走的空格相同&#xff0c;而走的敵人點不同&#xff0c;對答案無貢獻&#xff0c;而對于走的空格相同&#xff0c;但一種走了敵人點&#xff0c;另一種沒走&#xff0c;算兩個方案。。。。sb出題人語文簡直是和…

網站的SEO以及它和站長工具的之間秘密

博客遷移沒有注意 URL 地址的變化&#xff0c;導致百度和 google 這兩只爬蟲引擎短時間內找不到路。近段時間研究了下國內最大搜索引擎百度和國際最大搜索引擎google的站長工具&#xff0c;說下感受。 百度的站長工具地址&#xff1a;http://zhanzhang.baidu.com/dashboard/ind…

html 縮略圖點擊預覽,[每天進步一點點~] uni-app 點擊圖片實現預覽圖片列表

點擊圖片&#xff0c;實現預覽圖片功能&#xff0c;并且可循環預覽圖片列表&#xff01;image.png一、多張圖片預覽html代碼js代碼data(){return {photos:[{ src: 圖片路徑1},{ src: 圖片路徑2},{ src: 圖片路徑3},……]}},methods: {// 預覽圖片previewImage(index) {let phot…

git ssh拉取代碼_阿里云搭建git服務器

一.搭建步驟&#xff0c;分為兩步搭建中心倉庫自動同步代碼到站點目錄二.詳細步驟如下1.先檢查一下服務器上有沒有安裝gitgit --version如果出現版本號&#xff0c;說明服務器已經安裝git&#xff0c;如圖所示&#xff1a;2.如果沒有版本信息&#xff0c;則先安裝git&#xff1…

Django REST framework 序列化

創建一個序列化類 使用序列化有四種方式 使用json模塊&#xff0c;完全手寫使用django自帶的序列化模塊 1&#xff0c;# from django.core import serializers 2&#xff0c;# dataserializers.serialize(“json”,book_list)使用REST framework 帶的序列化方法&#xff0c…

基于SIMD的AVS整數反變換算法設計與優化

基于SIMD 的AVS 整數反變換算法設計與優化王玲娟&#xff0c;張剛**作者簡介&#xff1a;王玲娟&#xff0c;&#xff08;1987-&#xff09;&#xff0c;女&#xff0c;在讀碩士&#xff0c;主要研究方向&#xff1a;視頻解碼算法通信聯系人&#xff1a;張剛&#xff0c;&#…