88SIMPLE_MACRO_REGEX = re .compile (r"# *define *(\w+)(\(.+\))? " )
99SIMPLE_INLINE_REGEX = re .compile (r"static inline .+( |\n)(\w+)" )
1010SIMPLE_DATA_REGEX = re .compile (r"PyAPI_DATA\(.+\) (\w+)" )
11+ API_NAME_REGEX = re .compile (r'\bP[yY][a-zA-Z0-9_]+' )
1112
1213CPYTHON = Path (__file__ ).parent .parent .parent
1314INCLUDE = CPYTHON / "Include"
@@ -72,24 +73,10 @@ def found_ignored_documented(singular: bool) -> str:
7273 )
7374
7475
75- def is_documented (name : str ) -> bool :
76- """
77- Is a name present in the C API documentation?
78- """
79- for path in C_API_DOCS .iterdir ():
80- if path .is_dir ():
81- continue
82- if path .suffix != ".rst" :
83- continue
84-
85- text = path .read_text (encoding = "utf-8" )
86- if name in text :
87- return True
88-
89- return False
90-
91-
92- def scan_file_for_docs (filename : str , text : str ) -> tuple [list [str ], list [str ]]:
76+ def scan_file_for_docs (
77+ filename : str ,
78+ text : str ,
79+ names : set [str ]) -> tuple [list [str ], list [str ]]:
9380 """
9481 Scan a header file for C API functions.
9582 """
@@ -98,22 +85,22 @@ def scan_file_for_docs(filename: str, text: str) -> tuple[list[str], list[str]]:
9885 colors = _colorize .get_colors ()
9986
10087 def check_for_name (name : str ) -> None :
101- documented = is_documented ( name )
88+ documented = name in names
10289 if documented and (name in IGNORED ):
10390 documented_ignored .append (name )
10491 elif not documented and (name not in IGNORED ):
10592 undocumented .append (name )
10693
10794 for function in SIMPLE_FUNCTION_REGEX .finditer (text ):
10895 name = function .group (2 )
109- if not name . startswith ( "Py" ):
96+ if not API_NAME_REGEX . fullmatch ( name ):
11097 continue
11198
11299 check_for_name (name )
113100
114101 for macro in SIMPLE_MACRO_REGEX .finditer (text ):
115102 name = macro .group (1 )
116- if not name . startswith ( "Py" ):
103+ if not API_NAME_REGEX . fullmatch ( name ):
117104 continue
118105
119106 if "(" in name :
@@ -123,14 +110,14 @@ def check_for_name(name: str) -> None:
123110
124111 for inline in SIMPLE_INLINE_REGEX .finditer (text ):
125112 name = inline .group (2 )
126- if not name . startswith ( "Py" ):
113+ if not API_NAME_REGEX . fullmatch ( name ):
127114 continue
128115
129116 check_for_name (name )
130117
131118 for data in SIMPLE_DATA_REGEX .finditer (text ):
132119 name = data .group (1 )
133- if not name . startswith ( "Py" ):
120+ if not API_NAME_REGEX . fullmatch ( name ):
134121 continue
135122
136123 check_for_name (name )
@@ -152,6 +139,14 @@ def check_for_name(name: str) -> None:
152139
153140
154141def main () -> None :
142+ print ("Gathering C API names from docs..." )
143+ names = set ()
144+ for path in C_API_DOCS .glob ('**/*.rst' ):
145+ text = path .read_text (encoding = "utf-8" )
146+ for name in API_NAME_REGEX .findall (text ):
147+ names .add (name )
148+ print (f"Got { len (names )} names!" )
149+
155150 print ("Scanning for undocumented C API functions..." )
156151 files = [* INCLUDE .iterdir (), * (INCLUDE / "cpython" ).iterdir ()]
157152 all_missing : list [str ] = []
@@ -162,7 +157,7 @@ def main() -> None:
162157 continue
163158 assert file .exists ()
164159 text = file .read_text (encoding = "utf-8" )
165- missing , ignored = scan_file_for_docs (str (file .relative_to (INCLUDE )), text )
160+ missing , ignored = scan_file_for_docs (str (file .relative_to (INCLUDE )), text , names )
166161 all_found_ignored += ignored
167162 all_missing += missing
168163
0 commit comments