以下是一个简单的能运行的flask示例代码,从该示例代码中分析Flask源码完成了哪些工作。
flask示例代码如下:
from flask import Flask app = Flask(__name__) @app.route(‘/‘) def hello_world(): return ‘Hello World!‘ @app.route(‘/user/<name>‘) def user(name): return ‘<h1>Hello,%s!<h1>‘%name if __name__ == ‘__main__‘: app.run(debug=True)
首先调用app = Flask(__name__)构建一个Flask实例。Flask类定义在app.py文件中
"""The flask object implements a WSGI application and acts as the central object. It is passed the name of the module or package of the application. Once it is created it will act as a central registry for the view functions, the URL rules, template configuration and much more. The name of the package is used to resolve resources from inside the package or the folder the module is contained in depending on if the package parameter resolves to an actual python package (a folder with an :file:`__init__.py` file inside) or a standard module (just a ``.py`` file).
接下来分析app.route函数完成的工作
app.route是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
在这里主要是通过调用add_url_rule函数将app实例与对应的视图函数关联起来
def add_url_rule(self, rule, endpoint=None, view_func=None, **options): if endpoint is None: endpoint = _endpoint_from_view_func(view_func) options[‘endpoint‘] = endpoint methods = options.pop(‘methods‘, None) # if the methods are not given and the view_func object knows its # methods we can use that instead. If neither exists, we go with # a tuple of only ``GET`` as default. if methods is None: methods = getattr(view_func, ‘methods‘, None) or (‘GET‘,) if isinstance(methods, string_types): raise TypeError(‘Allowed methods have to be iterables of strings, ‘ ‘for example: @app.route(..., methods=["POST"])‘) methods = set(item.upper() for item in methods) # Methods that should always be added required_methods = set(getattr(view_func, ‘required_methods‘, ())) # starting with Flask 0.8 the view_func object can disable and # force-enable the automatic options handling. provide_automatic_options = getattr(view_func, ‘provide_automatic_options‘, None) if provide_automatic_options is None: if ‘OPTIONS‘ not in methods: provide_automatic_options = True required_methods.add(‘OPTIONS‘) else: provide_automatic_options = False # Add the required methods now. methods |= required_methods 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
如果没有指定endpoint函数_endpoint_from_view_func通过功能函数名指定为endpoint。接下来获取指定methods。url_rule_class(rule, methods=methods, **options)生成一rule的实例。接着url_map.add(rule)将rule与flask实例关联起来。在前面flask实例定义了Map对象的实例。add函数的源码如下:
def add(self, rulefactory): for rule in rulefactory.get_rules(self): rule.bind(self) self._rules.append(rule) self._rules_by_endpoint.setdefault(rule.endpoint, []).append(rule) self._remap = True
这里将map与rule关联起来。其中rule.bind()调用compile()对rule中的表达是进行了一次转义以‘/static/<path:filename>‘为例。
def compile(self): if self.map.host_matching: domain_rule = self.host or ‘‘ else: domain_rule = self.subdomain or ‘‘ self._trace = [] self._converters = {} self._weights = [] regex_parts = [] def _build_regex(rule): for converter, arguments, variable in parse_rule(rule): if converter is None: regex_parts.append(re.escape(variable)) self._trace.append((False, variable)) for part in variable.split(‘/‘): if part: self._weights.append((0, -len(part))) else: if arguments: c_args, c_kwargs = parse_converter_args(arguments) else: c_args = () c_kwargs = {} convobj = self.get_converter( variable, converter, c_args, c_kwargs) regex_parts.append(‘(?P<%s>%s)‘ % (variable, convobj.regex)) self._converters[variable] = convobj self._trace.append((True, variable)) self._weights.append((1, convobj.weight)) self.arguments.add(str(variable)) _build_regex(domain_rule) regex_parts.append(‘\\|‘) self._trace.append((False, ‘|‘)) _build_regex(self.is_leaf and self.rule or self.rule.rstrip(‘/‘)) if not self.is_leaf: self._trace.append((False, ‘/‘)) if self.build_only: return regex = r‘^%s%s$‘ % ( u‘‘.join(regex_parts), (not self.is_leaf or not self.strict_slashes) and ‘(?<!/)(?P<__suffix__>/?)‘ or ‘‘ ) self._regex = re.compile(regex, re.UNICODE)
_build_regex函数解析‘/static/<path:filename>‘。通过path去检索转换器
DEFAULT_CONVERTERS = { ‘default‘: UnicodeConverter, ‘string‘: UnicodeConverter, ‘any‘: AnyConverter, ‘path‘: PathConverter, ‘int‘: IntegerConverter, ‘float‘: FloatConverter, ‘uuid‘: UUIDConverter, }
在routing文件中定义了PathConverter类。通过path字典中获取。在这里主要获取的是regex = ‘[^/].*?‘这个属性。然后regex_parts.append(‘(?P<%s>%s)‘ % (variable, convobj.regex))将‘filename‘和‘[^/].*?‘填充对应的字符。这里生成对应的
url正则表达式
时间: 2024-12-27 01:51:34