Flask -- flask项目启动加载 源码解析
项目启动
实例化Flask对象
app = Flask(__name__)
执行__init__
:
1. 对app对象封装一些初始化的值。
app.static_url_path
app.static_folder
app.template_folder
app.config = Config()
app.view_functions = {} # 存放函数名与 endpoint (别名)的对应关系{'index':index}
app.before_request_funcs = {}
app.before_first_request_funcs = []
app.after_request_funcs = {}
2. 添加静态文件的路由
self.add_url_rule(
self.static_url_path + "/<path:filename>",
endpoint="static",
host=static_host,
view_func=self.send_static_file,
)
3. 实例化了url_map的对象,以后在map对象中放 【/index/ method endpoint】
class Flask(object):
url_rule_class = Rule
url_map_class = Map
def __init__(self...):
...
self.url_map = self.url_map_class()
app = Flask()
app.view_functions
app.url_rule_class
加载配置文件(给app的config进行赋值)
from flask import Flask
app = Flask(__name__)
app.config.from_object('xx.xx')
"""
app.config.from_object('xx.xx')
源码解析:
app.config = Config()
Config().from_object()
"""
# 源码:config.py
def from_object(self, obj):
if isinstance(obj, string_types):
obj = import_string(obj)
for key in dir(obj):
if key.isupper():
self[key] = getattr(obj, key) # 执行Config()的 __getitem__
1. 读取配置文件中的所有键值对,并将键值对全都放到Config对象。(Config是一个字典)
2. 把包含所有配置文件的Config对象,赋值给 app.config
3. from_object会执行 Config()字典的__getitem__方法,去获取值
特殊的装饰器加载(中间件)
def before_request(self, f):
self.before_request_funcs.setdefault(None, []).append(f)
return f
# 数据结构:{None:[f1,f2]}
def before_first_request(self, f):
self.before_first_request_funcs.append(f)
return f
# 数据结构:[f3, f4] 会在项目启动后,第一次请求执行,后面请求不执行
def after_request(self, f):
self.after_request_funcs.setdefault(None, []).append(f)
return f
# 数据结构:{None:[f5,f6]}
添加路由映射
from flask import Flask
app = Flask(__name__,static_url_path='/xx')
@app.route('/index')
def index():
return 'hello world'
源码解析:app.py
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
从add_url_rule
进入:
-
首先会将 endpoint (别名)添加到 options中,然后将methods获取到。
-
再执行
rule = url_rule_class = Rule()
将URL、methods、endpoint 封装到rule对象中; -
url_map.add(rule)
将rule
对象添加到url_map=Map()
对象中。 -
self.view_functions[endpoint] = view_func
,将函数名与 endpoint (别名)的对应关系添加到 view_functions中。
def add_url_rule(
self,
rule,
endpoint=None,
view_func=None,
provide_automatic_options=None,
**options
):
if endpoint is None:
endpoint = _endpoint_from_view_func(view_func)
options["endpoint"] = endpoint
methods = options.pop("methods", None)
# 。。。略
rule = self.url_rule_class(rule, methods=methods, **options)
rule.provide_automatic_options = provide_automatic_options
self.url_map.add(rule)
if view_func is not None:
old_func = self.view_functions.get(endpoint)
if old_func is not None and old_func != view_func:
raise AssertionError(
"View function mapping is overwriting an "
"existing endpoint function: %s" % endpoint
)
self.view_functions[endpoint] = view_func
1. 将 url = /index 和 methods = [GET,POST] 和 endpoint = "index"封装到Rule对象
2. 将Rule对象添加到 app.url_map中。
3. 把endpoint和函数的对应关系放到 app.view_functions中。
-
截止目前,app封装了三个属性
app.config # Config()对象,继承dict字典 app.url_map # 存放URL、methods、endpoint关系 app.view_functions # 存放函数名、endpoint关系
运行flask
from flask import Flask
app = Flask(__name__,static_url_path='/xx')
@app.route('/index')
def index():
return 'hello world'
if __name__ == '__main__':
app.run()
源码:
# run.py
def run(self, host=None, port=None, debug=None, load_dotenv=True, **options):
try:
run_simple(host, port, self, **options)
finally:
self._got_first_request = False
1. 执行run,内部调用werkzeug的run_simple,创建socket,监听IP和端口,等待用户请求到来。
2. 一旦有用户请求,就会执行执行self(),也就是app.__call__方法。本质就是将wsgi启动起来。
class Flask(object):
def __call__(self,envion,start_response):
return self.wsgi_app(environ, start_response)
def run(self):
run_simple(host, port, self, **options)
if __name__ == '__main__':
app.run()