Files
Odoo-Build/templates/18.0/coding-guidelines.py
T

141 lines
4.2 KiB
Python
Executable File

#!/usr/bin/env python3
import ast
import logging
import os
import re
import sys
logging.basicConfig(level=logging.INFO, format="%(message)s", stream=sys.stdout)
logger = logging.getLogger(__name__)
PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "."))
def load_gitignore_patterns():
"""
Load patterns from .gitignore file if it exists.
"""
gitignore_path = os.path.join(PROJECT_ROOT, ".gitignore")
patterns = set()
if os.path.exists(gitignore_path):
with open(gitignore_path, encoding="utf-8") as f:
for line in f:
line = line.strip()
if line and not line.startswith("#"):
if line.endswith("/"):
patterns.add(line[:-1])
elif "*" not in line and "?" not in line and "/" not in line:
patterns.add(line)
return patterns
EXCLUDED_PATTERNS = load_gitignore_patterns()
RED = "\033[91m"
YELLOW = "\033[93m"
GREEN = "\033[92m"
RESET = "\033[0m"
def find_python_files(base_dir):
"""
Find all Python files recursively, excluding directories and files from .gitignore.
"""
for root, dirs, files in os.walk(base_dir):
dirs[:] = [d for d in dirs if d not in EXCLUDED_PATTERNS]
for file in files:
if file in EXCLUDED_PATTERNS:
continue
if file.endswith(".py"):
filepath = os.path.join(root, file)
rel_path = os.path.relpath(filepath, base_dir)
if any(part in EXCLUDED_PATTERNS for part in rel_path.split(os.sep)):
continue
yield rel_path
def extract_routes_with_underscores(filepath):
"""
Extract routes with invalid underscores from a Python file.
"""
routes_with_underscores = []
try:
with open(filepath, encoding="utf-8") as f:
content = f.read()
tree = ast.parse(content, filename=filepath)
except (SyntaxError, UnicodeDecodeError, FileNotFoundError) as e:
logger.info(f"{YELLOW}Warning: Could not parse {filepath}: {str(e)}{RESET}")
return []
for node in ast.walk(tree):
if isinstance(node, ast.Call) and hasattr(node.func, "attr"):
# Check for FastAPI and HTTP route decorators
if node.func.attr in {"get", "post", "put", "delete", "patch", "route"}:
for arg in node.args:
if isinstance(arg, ast.Constant) and isinstance(arg.value, str):
if has_invalid_underscore_in_route(arg.value):
routes_with_underscores.append((arg.value, filepath))
return routes_with_underscores
def has_invalid_underscore_in_route(path: str) -> bool:
"""
Check if a route path contains invalid underscores (excluding path variables).
"""
stripped_path = re.sub(r"{[^}]*}", "", path)
return "_" in stripped_path
def check_framework_routes(base_path, framework_identifier, check_message):
"""
Check routes for a specific framework.
"""
violations = []
for py_file in find_python_files(base_path):
try:
with open(py_file, encoding="utf-8") as f:
content = f.read()
if framework_identifier in content:
violations.extend(extract_routes_with_underscores(py_file))
except (UnicodeDecodeError, FileNotFoundError) as e:
logger.info(f"{YELLOW}Warning: Could not read {py_file}: {str(e)}{RESET}")
if violations:
logger.info(f"{check_message} violations (underscores in paths):{RESET}")
for route, file in violations:
logger.info(f"{RED}{file}:{RESET} {YELLOW}{route}{RESET}")
return True
logger.info(f"{GREEN}No {check_message} violations found.{RESET}")
return False
def main():
"""
Main function to check for coding guidelines violations."
"""
base_path = PROJECT_ROOT
has_fastapi_violations = check_framework_routes(base_path, "fastapi", "FastAPI route")
has_http_route_violations = check_framework_routes(base_path, "http.route", "HTTP route decorator")
if has_fastapi_violations or has_http_route_violations:
return 1
return 0
if __name__ == "__main__":
sys.exit(main())