| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
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
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,170 @@ | ||||||||
| """ | ||||||||
| 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 | ||||||||
|
|
||||||||
| EXCLUDE_HEADERS = { | ||||||||
| # Header files not included by Python.h and ignored by this script | ||||||||
| 'dynamic_annotations.h', | ||||||||
| 'errcode.h', | ||||||||
| 'opcode.h', | ||||||||
| 'opcode_ids.h', | ||||||||
| 'osdefs.h', | ||||||||
| 'pyexpat.h', | ||||||||
| 'structmember.h', | ||||||||
|
|
||||||||
| # Header files not included by Python.h but parsed by this script: | ||||||||
| # - datetime.h | ||||||||
| # - frameobject.h | ||||||||
| # - marshal.h | ||||||||
| # - py_curses.h | ||||||||
| # - pydtrace.h | ||||||||
| } | ||||||||
|
|
||||||||
| 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*#\s*(?P<directive>define|undef)\s+(?P<macro>.*)') | ||||||||
| PYTHON_PREFIX = re.compile(r'(Py|PY|_Py|_PY)') | ||||||||
| NAME_REGEX = re.compile(r'([A-Za-z_][A-Za-z0-9_]*)\b') | ||||||||
|
|
||||||||
| CAUSES_BY_DIRECTIVE = { | ||||||||
| "define": "defined", | ||||||||
| "undef": "undefined", | ||||||||
| } | ||||||||
|
|
||||||||
|
|
||||||||
| def parse_file(filename, names): | ||||||||
| with open(filename, encoding='utf8') as fp: | ||||||||
| for lineno, line in enumerate(fp, start=1): | ||||||||
| # Check for '#define MACRO' | ||||||||
| match = DEFINE_REGEX.match(line) | ||||||||
|
Comment thread
vstinner marked this conversation as resolved.
|
||||||||
| if not match: | ||||||||
| continue | ||||||||
| macro = match['macro'] | ||||||||
| directive = match['directive'] | ||||||||
| cause = CAUSES_BY_DIRECTIVE.get(directive, directive) | ||||||||
|
|
||||||||
| if PYTHON_PREFIX.match(macro): | ||||||||
|
Comment thread
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[1] | ||||||||
| names.append((name, filename, lineno, cause)) | ||||||||
|
|
||||||||
|
|
||||||||
| 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')) | ||||||||
|
Comment thread
Copy link
Copy Markdown
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. Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality(Forgot to post this yesterday) Include/opcode_ids.h contains generated opcodes, we could exclude these:
Suggested change
And then remove the 245 generated names from check_capi_macros_ignored.txt. This will make maintenance easier so we don't need to add/remove future ones.
Sorry, something went wrong.
All reactions
|
||||||||
| files = [filename for filename in files | ||||||||
| if os.path.basename(filename) not in EXCLUDE_HEADERS] | ||||||||
| 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, lineno: int, cause: str) | ||||||||
| for filename in files: | ||||||||
| parse_file(filename, names) | ||||||||
|
|
||||||||
| # Parse ignore list | ||||||||
| ignored = get_ignored_names() | ||||||||
|
|
||||||||
| # Check if the sorted list has duplicated entries | ||||||||
| if len(set(ignored)) != len(ignored): | ||||||||
| print(f"ERROR: {IGNORED_FILENAME} list contains duplicated entries:") | ||||||||
| print() | ||||||||
| seen = set() | ||||||||
| for name in ignored: | ||||||||
| if name not in seen: | ||||||||
| seen.add(name) | ||||||||
| continue | ||||||||
| print(f"- {name}") | ||||||||
| print() | ||||||||
| failure = True | ||||||||
|
|
||||||||
| # 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, lineno, cause 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}") | ||||||||
| 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, lineno, cause in sorted(names): | ||||||||
| if name in ignored: | ||||||||
| continue | ||||||||
| print(f"- {name} {cause} at {filename}:{lineno}") | ||||||||
| 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() | ||||||||
| Back | FazBrowse Home | New Git URL |
Uh oh!
There was an error while loading. Please reload this page.