Skip to content

Commit 8c88402

Browse files
committed
General improvements.
1 parent e219136 commit 8c88402

File tree

1 file changed

+64
-38
lines changed

1 file changed

+64
-38
lines changed

Tools/check-c-api-docs/main.py

Lines changed: 64 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,37 @@
88
SIMPLE_INLINE_REGEX = re.compile(r"static inline .+( |\n)(\w+)")
99
SIMPLE_DATA_REGEX = re.compile(r"PyAPI_DATA\(.+\) (\w+)")
1010

11+
MISTAKE = """\
12+
If this is a mistake and this script should not be failing, please create an
13+
issue and tag Peter (@ZeroIntensity) on it.\
14+
"""
15+
16+
FOUND_UNDOCUMENTED = f"""\
17+
Found some undocumented C API!
18+
19+
Python requires documentation on all public C API functions.
20+
If these API(s) were not meant to be public, please prefix them with a
21+
leading underscore (_PySomething_API) or move them to the internal C API
22+
(pycore_*.h files).
23+
24+
In exceptional cases, certain functions can be ignored by adding them to
25+
Tools/c-api-docs-check/ignored_c_api.txt
26+
27+
{MISTAKE}\
28+
"""
29+
30+
FOUND_IGNORED_DOCUMENTED = f"""\
31+
Some C API(s) were listed in Tools/c-api-docs-check/ignored_c_api.txt, but
32+
they were found in the documentation. To fix this, simply update ignored_c_api.txt
33+
accordingly.
34+
35+
{MISTAKE}\
36+
"""
37+
1138
_CPYTHON = Path(__file__).parent.parent.parent
1239
INCLUDE = _CPYTHON / "Include"
1340
C_API_DOCS = _CPYTHON / "Doc" / "c-api"
14-
IGNORED = (_CPYTHON / "Tools" / "c-api-docs-check" / "ignored_c_api.txt").read_text().split("\n")
41+
IGNORED = (_CPYTHON / "Tools" / "check-c-api-docs" / "ignored_c_api.txt").read_text().split("\n")
1542

1643
for index, line in enumerate(IGNORED):
1744
if line.startswith("#"):
@@ -22,9 +49,6 @@ def is_documented(name: str) -> bool:
2249
"""
2350
Is a name present in the C API documentation?
2451
"""
25-
if name in IGNORED:
26-
return True
27-
2852
for path in C_API_DOCS.iterdir():
2953
if path.is_dir():
3054
continue
@@ -38,20 +62,27 @@ def is_documented(name: str) -> bool:
3862
return False
3963

4064

41-
def scan_file_for_missing_docs(filename: str, text: str) -> list[str]:
65+
def scan_file_for_docs(filename: str, text: str) -> tuple[list[str], list[str]]:
4266
"""
43-
Scan a header file for undocumented C API functions.
67+
Scan a header file for C API functions.
4468
"""
4569
undocumented: list[str] = []
70+
documented_ignored: list[str] = []
4671
colors = _colorize.get_colors()
4772

73+
def check_for_name(name: str) -> None:
74+
documented = is_documented(name)
75+
if documented and (name in IGNORED):
76+
documented_ignored.append(name)
77+
elif not documented and (name not in IGNORED):
78+
undocumented.append(name)
79+
4880
for function in SIMPLE_FUNCTION_REGEX.finditer(text):
4981
name = function.group(2)
5082
if not name.startswith("Py"):
5183
continue
5284

53-
if not is_documented(name):
54-
undocumented.append(name)
85+
check_for_name(name)
5586

5687
for macro in SIMPLE_MACRO_REGEX.finditer(text):
5788
name = macro.group(1)
@@ -61,76 +92,71 @@ def scan_file_for_missing_docs(filename: str, text: str) -> list[str]:
6192
if "(" in name:
6293
name = name[: name.index("(")]
6394

64-
if not is_documented(name):
65-
undocumented.append(name)
95+
check_for_name(name)
6696

6797
for inline in SIMPLE_INLINE_REGEX.finditer(text):
6898
name = inline.group(2)
6999
if not name.startswith("Py"):
70100
continue
71101

72-
if not is_documented(name):
73-
undocumented.append(name)
102+
check_for_name(name)
74103

75104
for data in SIMPLE_DATA_REGEX.finditer(text):
76105
name = data.group(1)
77106
if not name.startswith("Py"):
78107
continue
79108

80-
if not is_documented(name):
81-
undocumented.append(name)
109+
check_for_name(name)
82110

83111
# Remove duplicates and sort alphabetically to keep the output non-deterministic
84112
undocumented = list(set(undocumented))
85113
undocumented.sort()
86114

87-
if undocumented:
115+
if undocumented or documented_ignored:
88116
print(f"{filename} {colors.RED}BAD{colors.RESET}")
89117
for name in undocumented:
90118
print(f"{colors.BOLD_RED}UNDOCUMENTED:{colors.RESET} {name}")
91-
92-
return undocumented
119+
for name in documented_ignored:
120+
print(f"{colors.BOLD_YELLOW}DOCUMENTED BUT IGNORED:{colors.RESET} {name}")
93121
else:
94122
print(f"{filename} {colors.GREEN}OK{colors.RESET}")
95123

96-
return []
124+
return undocumented, documented_ignored
97125

98126

99127
def main() -> None:
100128
print("Scanning for undocumented C API functions...")
101129
files = [*INCLUDE.iterdir(), *(INCLUDE / "cpython").iterdir()]
102130
all_missing: list[str] = []
131+
all_found_ignored: list[str] = []
132+
103133
for file in files:
104134
if file.is_dir():
105135
continue
106136
assert file.exists()
107137
text = file.read_text(encoding="utf-8")
108-
missing = scan_file_for_missing_docs(str(file.relative_to(INCLUDE)), text)
138+
missing, ignored = scan_file_for_docs(str(file.relative_to(INCLUDE)), text)
139+
all_found_ignored += ignored
109140
all_missing += missing
110141

142+
fail = False
111143
if all_missing != []:
112144
s = "s" if len(all_missing) != 1 else ""
113-
print(f"-- {len(all_missing)} missing function{s} --")
145+
print(f"-- {len(all_missing)} missing C API{s} --")
114146
for name in all_missing:
115147
print(f" - {name}")
116-
print()
117-
print(
118-
"Found some undocumented C API!",
119-
"Python requires documentation on all public C API functions.",
120-
"If these function(s) were not meant to be public, please prefix "
121-
"them with a leading underscore (_PySomething_API) or move them to "
122-
"the internal C API (pycore_*.h files).",
123-
"",
124-
"In exceptional cases, certain functions can be ignored by adding "
125-
"them to Tools/c-api-docs-check/ignored_c_api.txt",
126-
"If this is a mistake and this script should not be failing, please "
127-
"create an issue and tag Peter (@ZeroIntensity) on it.",
128-
sep="\n",
129-
)
130-
sys.exit(1)
131-
else:
132-
print("Nothing found :)")
133-
sys.exit(0)
148+
print(FOUND_UNDOCUMENTED)
149+
fail = True
150+
151+
if all_found_ignored != []:
152+
s = "s" if len(all_found_ignored) != 1 else ""
153+
print(f"-- Found {len(all_found_ignored)} documented but ignored C API{s} --")
154+
for name in all_found_ignored:
155+
print(f" - {name}")
156+
print(FOUND_IGNORED_DOCUMENTED)
157+
fail = True
158+
159+
sys.exit(1 if fail else 0)
134160

135161

136162
if __name__ == "__main__":

0 commit comments

Comments
 (0)