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的封装。核心功能包括路由、模板、周边处理。关于模板和预处理、后处理相关代码后续分析。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值