-
-
Notifications
You must be signed in to change notification settings - Fork 35.9k
gh-157695: Check that C API macros start with "Py" #157696
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
e48804c
0d6621d
714f1f5
44eb795
819558a
c6af62b
998dd34
fa62aac
6188980
8c09a6c
7c8c77d
80a2e2f
d2347d9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,138 @@ | ||||||||||||||||||
| """ | ||||||||||||||||||
| Check that all macros defined by the Python C API have a name starting with | ||||||||||||||||||
| "Py". Ignore names listed by check_capi_macros_ignored.txt: macros with an | ||||||||||||||||||
| invalid name, added before this script was created. | ||||||||||||||||||
|
|
||||||||||||||||||
| Python C API: | ||||||||||||||||||
|
|
||||||||||||||||||
| * Include/*.h | ||||||||||||||||||
| * Include/cpython/*.h | ||||||||||||||||||
| * pyconfig.h.in | ||||||||||||||||||
| """ | ||||||||||||||||||
|
|
||||||||||||||||||
| import difflib | ||||||||||||||||||
| import glob | ||||||||||||||||||
| import os.path | ||||||||||||||||||
| import re | ||||||||||||||||||
| import sys | ||||||||||||||||||
|
|
||||||||||||||||||
| TOOLS_BUILD_DIR = os.path.abspath(os.path.dirname(__file__)) | ||||||||||||||||||
| SRC_DIR = os.path.dirname(os.path.dirname(TOOLS_BUILD_DIR)) | ||||||||||||||||||
| IGNORED_FILENAME = os.path.join(TOOLS_BUILD_DIR, 'check_capi_macros_ignored.txt') | ||||||||||||||||||
|
|
||||||||||||||||||
| DEFINE_REGEX = re.compile(r'\s*# *define\s+(.*)') | ||||||||||||||||||
| PYTHON_PREFIX = re.compile(r'(Py|PY|_Py|_PY)') | ||||||||||||||||||
| NAME_REGEX = re.compile(r'([A-Za-z_][A-Za-z0-9_]*)\b') | ||||||||||||||||||
| UNDEF_REGEX = re.compile(r'#undef (.*)') | ||||||||||||||||||
|
Comment on lines
+23
to
+26
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Expanding it:
Suggested change
Would find: Is it worth expanding it and ignoring these? |
||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
| def parse_file(filename, names): | ||||||||||||||||||
| with open(filename, encoding='utf8') as fp: | ||||||||||||||||||
| for line in fp: | ||||||||||||||||||
| # Check for '#define MACRO' | ||||||||||||||||||
| match = DEFINE_REGEX.match(line) | ||||||||||||||||||
|
vstinner marked this conversation as resolved.
|
||||||||||||||||||
| if match: | ||||||||||||||||||
| undef = False | ||||||||||||||||||
| else: | ||||||||||||||||||
| # Check for '#undef MACRO' | ||||||||||||||||||
| match = UNDEF_REGEX.match(line) | ||||||||||||||||||
| if not match: | ||||||||||||||||||
| continue | ||||||||||||||||||
| undef = True | ||||||||||||||||||
| macro = match.group(1) | ||||||||||||||||||
|
|
||||||||||||||||||
| if PYTHON_PREFIX.match(macro): | ||||||||||||||||||
|
vstinner marked this conversation as resolved.
|
||||||||||||||||||
| continue | ||||||||||||||||||
|
|
||||||||||||||||||
| match = NAME_REGEX.match(macro) | ||||||||||||||||||
| if not match: | ||||||||||||||||||
| print(f"ERROR: {filename}: Unable to parse {line!r}") | ||||||||||||||||||
| sys.exit(1) | ||||||||||||||||||
| name = match.group(1) | ||||||||||||||||||
| names.append((name, filename, undef)) | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
| def get_ignored_names(): | ||||||||||||||||||
| ignored = [] | ||||||||||||||||||
| with open(IGNORED_FILENAME, encoding='utf8') as fp: | ||||||||||||||||||
| for line in fp: | ||||||||||||||||||
| name = line.strip() | ||||||||||||||||||
| if name.startswith('#'): | ||||||||||||||||||
| # Ignore comment | ||||||||||||||||||
| continue | ||||||||||||||||||
| if name: | ||||||||||||||||||
| ignored.append(name) | ||||||||||||||||||
| return ignored | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
| def main(): | ||||||||||||||||||
| failure = False | ||||||||||||||||||
|
|
||||||||||||||||||
| # Parse header files | ||||||||||||||||||
| include_dir = os.path.join(SRC_DIR, 'Include') | ||||||||||||||||||
| files = glob.glob(os.path.join(include_dir, '*.h')) | ||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (Forgot to post this yesterday)
Suggested change
And then remove the 245 generated names from |
||||||||||||||||||
| files.extend(glob.glob(os.path.join(include_dir, 'cpython', '*.h'))) | ||||||||||||||||||
| files.append(os.path.join(SRC_DIR, 'pyconfig.h.in')) | ||||||||||||||||||
| names = [] # list of (name: str, filename: str, undef: bool) | ||||||||||||||||||
| for filename in files: | ||||||||||||||||||
| parse_file(filename, names) | ||||||||||||||||||
|
|
||||||||||||||||||
| # Parse ignore list | ||||||||||||||||||
| ignored = get_ignored_names() | ||||||||||||||||||
|
|
||||||||||||||||||
| # Check if the sorted list is sorted | ||||||||||||||||||
| ignored_sorted = sorted(ignored) | ||||||||||||||||||
| if ignored_sorted != ignored: | ||||||||||||||||||
| print(f"ERROR: {IGNORED_FILENAME} list is not sorted") | ||||||||||||||||||
| print() | ||||||||||||||||||
| diff = difflib.unified_diff(ignored, ignored_sorted, | ||||||||||||||||||
| fromfile=IGNORED_FILENAME, | ||||||||||||||||||
| tofile=IGNORED_FILENAME, | ||||||||||||||||||
| lineterm='') | ||||||||||||||||||
| for line in diff: | ||||||||||||||||||
| print(line) | ||||||||||||||||||
| print() | ||||||||||||||||||
| failure = True | ||||||||||||||||||
|
|
||||||||||||||||||
| # Check for outdated ignore list | ||||||||||||||||||
| names_set = {name for name, filename, undef in names} | ||||||||||||||||||
| ignored = set(ignored) | ||||||||||||||||||
| outdated = ignored - names_set | ||||||||||||||||||
| if outdated: | ||||||||||||||||||
| print(f"ERROR: {IGNORED_FILENAME} is outdated, " | ||||||||||||||||||
| "the following macros can be removed:") | ||||||||||||||||||
| print() | ||||||||||||||||||
| for name in sorted(outdated): | ||||||||||||||||||
| print(f" - {name}") | ||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: remove leading space:
Suggested change
To match the other: print(f"- {name} {define} by {filename}") |
||||||||||||||||||
| print() | ||||||||||||||||||
| print(f"Total: {len(outdated)} macros") | ||||||||||||||||||
| print() | ||||||||||||||||||
| failure = True | ||||||||||||||||||
|
|
||||||||||||||||||
| # Check for new macros | ||||||||||||||||||
| new_macros = names_set - ignored | ||||||||||||||||||
| if new_macros: | ||||||||||||||||||
| print('ERROR: the Python C API defines the following new macros:') | ||||||||||||||||||
| print() | ||||||||||||||||||
| count = 0 | ||||||||||||||||||
| for name, filename, undef in sorted(names): | ||||||||||||||||||
| if name in ignored: | ||||||||||||||||||
| continue | ||||||||||||||||||
| define = "undefined" if undef else "defined" | ||||||||||||||||||
| print(f"- {name} {define} by {filename}") | ||||||||||||||||||
| count += 1 | ||||||||||||||||||
| print() | ||||||||||||||||||
| print(f"Total: {count} macros") | ||||||||||||||||||
| failure = True | ||||||||||||||||||
|
|
||||||||||||||||||
| if not failure: | ||||||||||||||||||
| print("OK: the ignore list is up to date and sorted") | ||||||||||||||||||
| print("OK: the Python C API only defines macros with names " | ||||||||||||||||||
| f"starting with Py (ignoring {len(ignored)} macros)") | ||||||||||||||||||
| sys.exit(0) | ||||||||||||||||||
|
|
||||||||||||||||||
| sys.exit(1) | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
| if __name__ == "__main__": | ||||||||||||||||||
| main() | ||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.