Flask源码分析系列(2) -Flask源码分析

转载请注明出处即可
源码地址github flask
主要参考文档为flask
环境为MacOS, Python 3.7+, IDE Pycharm

注意:文章中的源码存在删减,主要是为了减少篇幅和去除非核心逻辑,但不会影响对执行流程的理解。

如果对Werkzeug不是很了解,请先看Flask源码分析系列(1) -Werkzeug源码分析这篇文章

一、从一个最简单的Demo开始

Flask是Python语言编写的一个优秀的开源Web框架。我们先从一个最小的Demo开始,逐步来分析Flask是如何实现相关功能的。

from flask import Flask

app = Flask(__name__)


@app.route('/')
def hello_world():
    return 'Hello, World!'


def main():
    app.run(host='0.0.0.0', port=8080, debug=True)


if __name__ == '__main__':
    main()

首先app变量或者说Flask类创建的对象,其实是一个WSGI Application,也就是说是一个符合上篇文件中描述的一个符合WSGI规则的一个函数,具体是Flask类的wsgi_app方法来实现。

# app.py 2366行, Flask类的方法
def __call__(self, environ, start_response):
    return self.wsgi_app(environ, start_response)

# app.py 2323行, Flask类的方法
def wsgi_app(self, environ, start_response):
    pass

虽然app.run方法提供了Werkzeug的serving.make_server的实现,但是你依然可以选择其他支持WSGI协议的Server来运行Flask应用,比如gunicorn等。在实践中,我们在开发环境可以选择一些基本的WSGI Server用于本地调试。而在生产环境中在使用gunicorn等来实现多进程运行。当然这都直接取决于你自己根据实际的环境进行选择。以下代码是使用tornado的httpserver的一个例子。

from tornado.wsgi import WSGIContainer
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop
from demo import app

import sys

reload(sys)
sys.setdefaultencoding("utf-8")


def main():
    http_server = HTTPServer(WSGIContainer(app))
    http_server.listen(8080)
    IOLoop.instance().start()


app.config['SESSION_TYPE'] = 'filesystem'
app.config['APIURL'] = '/api'

if __name__ == "__main__":
    main()

如果使用gunicorn,那么可以通过以下指令来运行Flask应用。

export FLASK_ENV=development
THREAD_COUNT=8
gunicorn -k gevent -w $THREAD_COUNT -b 0.0.0.0:8080 demo:app -t 6000000

扯了一些基本应用,下面开始进入正题。

二、Route实现原理

Route的实际作用是将url path和具体要执行的函数进行映射。Flask并没有把这些能力自己实现,而是使用了Werkzeug的Map、Rule和MapAdapter来实现。

首先先看下@app.route(’/’)装饰器的实现。
(Python的装饰器在这里不详细解释,如果不明白请查看廖雪峰的Python教程)

def route(self, rule, **options):
    def decorator(f):
        endpoint = options.pop("endpoint", None)
        self.add_url_rule(rule, endpoint, f, **options)
        return f

    return decorator

代码很简洁,route方法的参数rule是url path,而options则对应着Werkzeug中Rule类的参数,比如endpoint,methods等。除了endpoint做了一些特殊的处理以外,其他的参数原封不动的传到了Rule的__init__
在decorator函数中的第一行从options dict中pop出了endpoint,这里是因为在add_url_rule进行了一些其他处理(其实就是判断是否是None,然后选择是否使用函数名称而已)。
add_url_rule方法的第三个参数f,则为被装饰的函数,在Demo的例子中就是hello_world函数。
Flask默认使用的endpoint是方法的名称,但依然保留了这个参数,方便用户自定义endpoint。

然后我们来看下Flask.add_url_rule方法的实现。具体源码在app.py的1099行。由于方法略长,我们来拆分即可来分析。方法的参数列表没有什么需要过多解释的。

def add_url_rule(
    self,
    rule,
    endpoint=None,
    view_func=None,
    provide_automatic_options=None,
    **options,
):
  pass

函数的第一段,是处理endpoint,如果用户没有在route中设置endpoint参数的话,则默认使用了view_func.__name__来获取函数的名称。然后获取了methods的参数。

if endpoint is None:
    endpoint = _endpoint_from_view_func(view_func)
options["endpoint"] = endpoint

函数的第二段,是对methods参数的处理,如果用户没有设置methods列表(或元组)的话,默认设置为(“Get”,)。并且对methods进行了是否是字符串的检查, 最后将所有的method都变成大写和去重(放入了Set中)。

methods = options.pop("methods", None)
if methods is None:
    methods = 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值