Skip to content
Snippets Groups Projects
Select Git revision
  • 2da3aaa6f3212c5b4a0bdac661a2f44367ee38fb
  • master default protected
  • md-export
  • th/mail
  • 179-einladungen-zum-aushaengen-drucken
5 results

check_routes.py

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    check_routes.py 4.52 KiB
    #!/usr/bin/env python3
    import regex as re
    import os
    import sys
    
    ROUTE_PATTERN = r'@(?:[[:alpha:]])+\.route\(\"(?<url>[^"]+)"[^)]*\)\s*(?:@[[:alpha:]_()., ]+\s*)*def\s+(?<name>[[:alpha:]][[:alnum:]_]*)\((?<params>[[:alnum:], ]*)\):'
    quote_group = "[\"']"
    URL_FOR_PATTERN = r'url_for\({quotes}(?<name>[[:alpha:]][[:alnum:]_]*){quotes}'.format(quotes=quote_group)
    
    ROOT_DIR = "."
    ENDINGS = [".py", ".html", ".txt"]
    MAX_DEPTH = 2
    
    def list_dir(dir, level=0):
        if level >= MAX_DEPTH:
            return
        for file in os.listdir(dir):
            path = os.path.join(dir, file)
            if os.path.isfile(path):
                if file == sys.argv[0]:
                    continue
                for ending in ENDINGS:
                    if file.endswith(ending):
                        yield path
            elif os.path.isdir(path):
                yield from list_dir(path, level+1)
    
    class Route:
        def __init__(self, file, name, parameters):
            self.file = file
            self.name = name
            self.parameters = parameters
    
        def __repr__(self):
            return "Route({file}, {name}, {parameters})".format(
                file=self.file, name=self.name, parameters=self.parameters)
    
        def get_parameter_set(self):
            return {parameter.name for parameter in self.parameters}
    
    class Parameter:
        def __init__(self, name, type=None):
            self.name = name
            self.type = type
    
        def __repr__(self):
            return "Parameter({name}, {type})".format(name=self.name, type=self.type)
    
        @staticmethod
        def from_string(text):
            if ":" in text:
                type, name = text.split(":", 1)
                return Parameter(name, type)
            return Parameter(text)
    
    def split_url_parameters(url):
        params = []
        current_param = None
        for char in url:
            if current_param is None:
                if char == "<":
                    current_param = ""
            else:
                if char == ">":
                    params.append(Parameter.from_string(current_param))
                    current_param = None
                else:
                    current_param += char
        return params
    
    def split_function_parameters(parameters):
        return list(map(str.strip, parameters.split(",")))
    
    def read_url_for_parameters(content):
        params = []
        bracket_level = 1
        current_param = None
        for char in content:
            if bracket_level == 0:
                if current_param is not None:
                    params.append(current_param.split("=")[0].strip())
                return params
            if char == ",":
                if current_param is not None:
                    params.append(current_param.split("=")[0].strip())
                current_param = ""
            else:
                if current_param is not None:
                    current_param += char
                if char == "(":
                    bracket_level += 1
                elif char == ")":
                    bracket_level -= 1
    
    class UrlFor:
        def __init__(self, file, name, parameters):
            self.file = file
            self.name = name
            self.parameters = parameters
    
        def __repr__(self):
            return "UrlFor(file={file}, name={name}, parameters={parameters})".format(
                file=self.file, name=self.name, parameters=self.parameters)
    
    routes = {}
    url_fors = []
    for file in list_dir(ROOT_DIR):
        with open(file, "r") as infile:
            content = infile.read()
            for match in re.finditer(ROUTE_PATTERN, content):
                name = match.group("name")
                function_parameters = split_function_parameters(match.group("params"))
                url_parameters = split_url_parameters(match.group("url"))
                routes[name] = Route(file, name, url_parameters)
            for match in re.finditer(URL_FOR_PATTERN, content):
                name = match.group("name")
                begin, end = match.span()
                parameters = read_url_for_parameters(content[end:])
                url_fors.append(UrlFor(file=file, name=name, parameters=parameters))
    
    for url_for in url_fors:
        if url_for.name not in routes:
            print("Missing route '{}' (for url_for in '{}')".format(url_for.name, url_for.file))
            continue
        route = routes[url_for.name]
        route_parameters = route.get_parameter_set()
        url_parameters = set(url_for.parameters)
        if len(route_parameters ^ url_parameters) > 0:
            print("Parameters not matching for '{}' in '{}:'".format(url_for.name, url_for.file))
            only_route = route_parameters - url_parameters
            only_url = url_parameters - route_parameters
            if len(only_route) > 0:
                print("Only in route: {}".format(only_route))
            if len(only_url) > 0:
                print("Only in url: {}".format(only_url))