1. 程式人生 > >Flask原始碼分析(一)

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的封裝。核心功能包括路由、模板、周邊處理。關於模板和預處理、後處理相關程式碼後續分析。