Flask原始碼分析(一)
知識背景
Flask是python web框架,主要包含werkzeug和jinja2,前者是一個WSGI工具集,後者用來實現模板處理。
WSGI,Werkzeug
WSGI
WSGI(Web Server Gateway Interface)是一個協議,定義了Web Server和app之間的介面。介面很簡單,下面一個例子myapp.py:
def app(env, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
return '<h1>Hello, web!</h1>'
# gunicorn myapp:app
gunicorn在這裡作為一個WSGI web server,通過上面的命令就可以啟動這個app。關於WSGI詳細資訊可以參考廖雪峰的官方網站
Werkzeug
Werkzeug 並不是 一個框架,它是一個 WSGI 工具集的庫,你可以通過它來建立你自己的框架或 Web 應用。
Werkzeug提供了一系列工具,使WSGI程式設計更簡單,下面一個列子:
from werkzeug.wrappers import Request, Response
def application(environ, start_response) :
request = Request(environ)
text = 'Hello %s!' % request.args.get('name', 'World')
response = Response(text, mimetype='text/plain')
return response(environ, start_response)
上面的例子中通過Request和Response,使app程式碼更加pythonic。還有一些有效的工具,用來實現路由等功能,這些在flask中都有應用:
from werkzeug.routing import Map , Rule
關於Werkzeug詳細資訊可以參考Werkzeug教程
jinja2
模板處理是flask的核心功能之一,用來處理網頁模板:
from jinja2 import Template
t = Template("{{ name }}, hello jinja2 world!")
t.render(name='Mr. Y') #u'Mr. Y, hello jinja2 world!'
關於jiaja2詳細資訊可以參考歡迎來到 Jinja2
原始碼分析
Flask參考資料flask,官網有一個最簡單app:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run()
0.1版程式碼量最小,只有幾百行實現了所有核心功能。就用這個app去分析0.1版程式碼(這個APP不涉及模板)
1,初始化app
from flask import Flask
app = Flask(__name__)
flask.py
class Flask(object):
def __init__(self, package_name):
#包名
self.package_name = package_name
#根路徑
self.root_path = _get_package_path(self.package_name)
#檢視函式,即例子中的hello_world函式
self.view_functions = {}
#錯誤處理函式,Key是錯誤狀態碼,Value是處理函式
self.error_handlers = {}
#預處理函式列表
self.before_request_funcs = []
#後處理函式列表
self.after_request_funcs = []
#url到檢視函式的對映
self.url_map = Map()
2,路由
@app.route('/')
def hello_world():
return 'Hello World!'
flask.py
def route(self, rule, **options):
def decorator(f):
self.add_url_rule(rule, f.__name__, **options)
self.view_functions[f.__name__] = f
return f
return decorator
def add_url_rule(self, rule, endpoint, **options):
options['endpoint'] = endpoint
options.setdefault('methods', ('GET',))
self.url_map.add(Rule(rule, **options))
route是一個修飾器,功能就是完成url_map和view_functions的初始化,其中Rule是werkzeug提供的工具。
3,run
3.1主流程
if __name__ == '__main__':
app.run()
app執行時的呼叫順序是:
run
--> werkzeug.run_simple
--> __call__(self, environ, start_respones)
--> wsgi_app(environ, start_response)
__call__()函式很簡單,核心是wsgi_app:
def __call__(self, environ, start_response):
"""Shortcut for :attr:`wsgi_app`"""
return self.wsgi_app(environ, start_response)
def wsgi_app(self, environ, start_response):
#初始化請求,3.2節分析
with self.request_context(environ):
#預處理,hello_world例子中不包含
rv = self.preprocess_request()
#分發請求,3.3節分析
if rv is None:
rv = self.dispatch_request()
response = self.make_response(rv)
#後處理,hello_world例子中不包含
response = self.process_response(response)
#響應
return response(environ, start_response)
3.2初始化請求
請求上下文呼叫順序,請求儲存在_request_ctx_stack中,LocalStack由werkzeug提供,並保證執行緒安全。
request_context
--> _RequestContext(self, environ)
class _RequestContext(object):
def __init__(self, app, environ):
self.app = app
self.url_adapter = app.url_map.bind_to_environ(environ)
self.request = app.request_class(environ)
self.session = app.open_session(self.request)
self.g = _RequestGlobals()
self.flashes = None
def __enter__(self):
_request_ctx_stack.push(self)
def __exit__(self, exc_type, exc_value, tb):
if tb is None or not self.app.debug:
_request_ctx_stack.pop()
_request_ctx_stack = LocalStack()
3.3分發請求
def dispatch_request(self):
try:
endpoint, values = self.match_request()
return self.view_functions[endpoint](**values)
except HTTPException, e:
handler = self.error_handlers.get(e.code)
if handler is None:
return e
return handler(e)
except Exception, e:
handler = self.error_handlers.get(500)
if self.debug or handler is None:
raise
return handler(e)
查詢檢視函式view_functions獲得響應的處理函式。如果異常則返回響應的異常處理函式。
總結
至此,基本把hello_world呼叫過程分析完。可以看到flask只是做了WSGI app的封裝。核心功能包括路由、模板、周邊處理。關於模板和預處理、後處理相關程式碼後續分析。