python-flask-路由匹配源码分析
@app.route('/') def hello_world(): return 'Hello World!'
第1步:
class Flask(_PackageBoundObject): def route(self, rule, **options): def decorator(f): #f = hello_world
#1.1获取别名 endpoint = options.pop('endpoint', None)
#1.2 self.add_url_rule(rule, endpoint, f, **options) return f return decorator
第1.2步:
class Flask(_PackageBoundObject): def add_url_rule(self, rule, endpoint=None, view_func=None, **options): #1.2.1如果别名是None,执行_endpoint_from_view_func函数 if endpoint is None: endpoint = _endpoint_from_view_func(view_func)
#1.2.2 给endpoint赋值 options['endpoint'] = endpoint
#1.2.3 获取允许的请求方法 methods = options.pop('methods', None) #1.2.4如果方法为None if methods is None:
#默认为GET methods = getattr(view_func, 'methods', None) or ('GET',)
#1.2.5如果方法是字符串,抛出异常: 必须是methods=["POST"]这样可迭代的 if isinstance(methods, string_types): raise TypeError('Allowed methods have to be iterables of strings, ' 'for example: @app.route(..., methods=["POST"])')
#1.2.6把方法变成大写 methods = set(item.upper() for item in methods) #1.2.7把匹配url和允许请求的方法封装到了Rule的一个对象中 rule = self.url_rule_class(rule, methods=methods, **options)
#self.url_rule_class = Rul
#1.2.8 self.url_map.add(rule)
#self.url_map = Map() if view_func is not None: #1.2.9 此步完成后: {"别名":被装饰的函数名} self.view_functions[endpoint] = view_func
第1.2.1步:
def _endpoint_from_view_func(view_func): #返回被装饰的函数名 return view_func.__name__
第1.2.8步:
class Map(object): def add(self, rulefactory): #1.2.8.1 for rule in rulefactory.get_rules(self):
#1.2.8.2 Rule对象进行re正则绑定 rule.bind(self)
#1.2.8.3 添加到self._rules列表中,此时列表中就有了url self._rules.append(rule)
#1.2.8.4 此步完成后: {"别名":url} self._rules_by_endpoint.setdefault(rule.endpoint, []).append(rule)
第1.2.8.1步:
class Rule(RuleFactory):
def get_rules(self, map): yield self
#返回Rule对
第1.2.8.2步:
class Rule(RuleFactory):
def bind(self, map, rebind=False):
#1.2.8.2.1 self.compile()
第1.2.8.2.1步: 看不懂,猜里面是把url进行了re正则处理
class Rule(RuleFactory): def compile(self): if self.map.host_matching: domain_rule = self.host or '' else: domain_rule = self.subdomain or '' self._trace = [] self._converters = {} self._static_weights = [] self._argument_weights = [] regex_parts = [] def _build_regex(rule): index = 0 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._static_weights.append((index, -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._argument_weights.append(convobj.weight) self.arguments.add(str(variable)) index = index + 1 _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)
到此为止:
路由规则列表里新增了一条绑定正则的rule ;{"别名":rule} ;{"别名":被装饰的函数名}