-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathflask_route_checker.py
More file actions
80 lines (67 loc) · 2.82 KB
/
flask_route_checker.py
File metadata and controls
80 lines (67 loc) · 2.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# -*- coding: utf-8 -*-
from __future__ import annotations
import ast
import os
import re
import sys
ROUTE_DECORATOR_NAMES = {"route", "add_url_rule"}
ROUTE_ARG_PATTERN = re.compile(r"<(\w+)>")
def extract_placeholders(route_string):
return list(ROUTE_ARG_PATTERN.findall(route_string))
class RouteParamChecker(ast.NodeVisitor):
def __init__(self, filename):
self.filename = filename
self.mismatches = []
def visit_FunctionDef(self, node):
route_placeholders = list()
for deco in node.decorator_list:
if isinstance(deco, ast.Call) and isinstance(deco.func, ast.Attribute):
if deco.func.attr in ROUTE_DECORATOR_NAMES:
if deco.args and isinstance(deco.args[0], ast.Constant):
route = deco.args[0].value
route_placeholders.extend(extract_placeholders(route))
if route_placeholders:
func_args = [arg.arg for arg in node.args.args]
extra = set(route_placeholders) - set(func_args)
missing = set(func_args) - set(route_placeholders)
order = any(x != y for (x, y) in zip(route_placeholders, func_args))
if extra or missing or order:
self.mismatches.append(
{
"line": node.lineno,
"function": node.name,
"route_placeholders": route_placeholders,
"function_args": func_args,
"extra_placeholders": extra,
"missing_args": missing,
"order": order,
}
)
self.generic_visit(node)
def check_file(filepath):
with open(filepath, "r", encoding="utf-8") as f:
source = f.read()
tree = ast.parse(source, filename=filepath)
checker = RouteParamChecker(filepath)
checker.visit(tree)
return checker.mismatches
def main(directory):
for root, _, files in os.walk(directory):
for fname in files:
if fname.endswith(".py"):
path = os.path.join(root, fname)
mismatches = check_file(path)
for m in mismatches:
print(f"{path}:{m['line']} → function '{m['function']}'")
if m["extra_placeholders"]:
print(
f" Placeholders not in function signature: {sorted(m['extra_placeholders'])}"
)
if m["missing_args"]:
print(f" Parameters not in route pattern: {sorted(m['missing_args'])}")
print()
if __name__ == "__main__":
if len(sys.argv) != 2:
print("Usage: python flask_route_checker.py <path_to_your_flask_app_directory>")
sys.exit(1)
main(sys.argv[1])