Skip to content

Commit f799653

Browse files
authored
Merge pull request #1051 from tekktrik/dev/path-script
Add JSON path checking pre-commit hook
2 parents 796891e + 70b0080 commit f799653

2 files changed

Lines changed: 100 additions & 0 deletions

File tree

.pre-commit-config.yaml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,18 @@ repos:
4141
files: "^tests/"
4242
args:
4343
- --disable=missing-docstring,consider-using-f-string,duplicate-code
44+
- repo: local
45+
hooks:
46+
- id: board-json-validation
47+
name: Perform board_imports.json import path validition
48+
description: Check validity of import paths in boards_imports.json
49+
language: python
50+
files: src/board_imports.json$
51+
entry: python scripts/check_imports_paths.py
52+
args: ["--suffix", ".py"]
53+
- id: microcontroller-json-validation
54+
name: Perform microcontroller_imports.json import path validation
55+
description: Check validity of import paths in microcontroller_imports.json
56+
language: python
57+
files: src/microcontroller_imports.json$
58+
entry: python scripts/check_imports_paths.py

scripts/check_imports_paths.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# SPDX-FileCopyrightText: 2026 Alec Delaney
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
import argparse
6+
import json
7+
import pathlib
8+
import sys
9+
from typing import Dict, List, Union
10+
11+
NodeType = Dict[str, Union[str, "NodeType"]]
12+
13+
14+
def collect_endnodes(node: NodeType, collected_list: List[str]) -> List[str]:
15+
"""Recursively collect all end nodes (which should be import paths)."""
16+
for value in node.values():
17+
if isinstance(value, str):
18+
collected_list.append(value)
19+
else:
20+
collect_endnodes(value, collected_list)
21+
return collected_list
22+
23+
24+
def main():
25+
# Parse the arguments
26+
parser = argparse.ArgumentParser(
27+
prog="check-import-paths",
28+
description="Checks a JSON file for validity of listed import paths",
29+
)
30+
parser.add_argument(
31+
"filenames",
32+
nargs="*",
33+
help="The filepath of the JSON file to check",
34+
)
35+
parser.add_argument(
36+
"--suffix",
37+
help="Suffix to append to JSON endnodes (e.g., .py)",
38+
)
39+
args = parser.parse_args(sys.argv[1:])
40+
filepath_args: str = args.filenames
41+
suffix_arg: str = args.suffix
42+
43+
# Ensure only one file is provided
44+
if len(filepath_args) > 1:
45+
print("This hooks is only intended to check a single file")
46+
return 1
47+
filepath_arg = filepath_args[0]
48+
49+
# Open the provided file
50+
try:
51+
with open(filepath_arg) as jsonfile:
52+
contents = json.load(jsonfile)
53+
except FileNotFoundError:
54+
print("Could not find the requested file")
55+
return 1
56+
except json.JSONDecodeError:
57+
print("Could not parse the given file as valid JSON")
58+
return 1
59+
60+
# Collect all JSON endnodes
61+
imports = collect_endnodes(contents, [])
62+
63+
# Create string paths
64+
strpaths = [i.replace(".", "/") for i in imports]
65+
if suffix_arg:
66+
strpaths = [strpath + suffix_arg for strpath in strpaths]
67+
68+
# Transform import paths to filepaths
69+
root = pathlib.Path("src")
70+
filepaths = [root / pathlib.Path(strpath) for strpath in strpaths]
71+
import_pairs = list(zip(imports, filepaths))
72+
73+
# Check for nonexistent paths
74+
invalid_paths = [p for p in import_pairs if not p[1].exists()]
75+
if invalid_paths:
76+
print(f"The following paths were not found:")
77+
for import_path, _ in invalid_paths:
78+
print(f"- {import_path}")
79+
return 1
80+
else:
81+
return 0
82+
83+
84+
if __name__ == "__main__":
85+
raise SystemExit(main())

0 commit comments

Comments
 (0)