forked from intel/dffml
-
Notifications
You must be signed in to change notification settings - Fork 0
/
check_literalincludes.py
executable file
·97 lines (91 loc) · 3.49 KB
/
check_literalincludes.py
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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import sys
import subprocess
def literalinclude_blocks():
literalincludes = subprocess.check_output(
["git", "grep", "-A", "10", ".. literalinclude::"]
)
literalincludes = literalincludes.decode()
section = []
for line in "\n".join(literalincludes.split("\n--\n")).split("\n"):
if not line.strip():
continue
# If literalinclude is in the output git grep will separate with :
# instead of -
if ".. literalinclude::" in line:
line = line.split(":", maxsplit=1)
else:
line = line.split("-", maxsplit=1)
# For blank lines
if section and (len(line) != 2 or not line[1]):
yield section
section = []
continue
contains_literalinclude, filecontents = line
if ".. literalinclude::" in filecontents:
section = [contains_literalinclude, [filecontents]]
elif section:
section[1].append(filecontents)
def main():
# Map filenames that might have changed to the file which references it's
# line numbers
check_changed = {}
for contains_literalinclude, lines in literalinclude_blocks():
# Skip blocks that don't reference specific lines
if not ":lines:" in "\n".join(lines):
continue
# Grab the file being referenced
# Remove /../ used by sphinx docs to reference files outside docs dir
referenced = lines[0].split()[-1].replace("/../", "", 1)
check_changed.setdefault(referenced, {})
check_changed[referenced].setdefault(contains_literalinclude, False)
# Get the list of changed files
changed_files = subprocess.check_output(
["git", "diff-index", "origin/master"]
)
changed_files = changed_files.decode()
changed_files = list(
map(
lambda line: line.split()[-1],
filter(bool, changed_files.split("\n")),
)
)
rm_paths = []
for filepath in check_changed:
if not filepath in changed_files:
rm_paths.append(filepath)
for filepath in rm_paths:
del check_changed[filepath]
for filepath in check_changed:
if filepath in changed_files:
for has_been_updated in check_changed[filepath]:
if has_been_updated in changed_files:
check_changed[filepath][has_been_updated] = True
# Fail if any are referenced_by
fail = False
for referenced_changed, should_have_changed in check_changed.items():
for filepath, was_changed in should_have_changed.items():
if not was_changed:
fail = True
print(
f"{filepath!r} might need updating as line numbers of "
+ f"{referenced_changed!r} may have changed"
)
if not fail:
return
print(
"This script checks to see if any .py files changed that are "
"referenced in .rst files using specific line numbers. This script "
"is failing because those rst files were not also modified. "
"This script is not smart enough to tell if those lines should be "
"modified, or that you modifed them correctly. Just as an extra "
"sanity check."
)
print()
print(
"You'll notice that the literalinclude blocks might end up with the "
"text in them being off because the :lines: wasn't changed. That's "
"what this script is hoping to catch."
)
sys.exit(1)
if __name__ == "__main__":
main()