Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Check also if the ignored list is outdated
Fix UNDEF_REGEX macro: replace fullmatch() with match()
  • Loading branch information
vstinner committed Sep 18, 2026
commit 7c8c77ddb56d9374b93d7f0515a9ceb689738a17
62 changes: 39 additions & 23 deletions Tools/build/check_capi_macros.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@

TOOLS_BUILD_DIR = os.path.abspath(os.path.dirname(__file__))
SRC_DIR = os.path.dirname(os.path.dirname(TOOLS_BUILD_DIR))
Comment thread
ZeroIntensity marked this conversation as resolved.
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)')
DEFINE_NAME_REGEX = re.compile(r'([A-Za-z_][A-Za-z0-9_]*)\b')
UNDEF_REGEX = re.compile(r'#undef (.*)')


def parse_file(filename, names, ignored):
def parse_file(filename, names):
with open(filename, encoding='utf8') as fp:
for line in fp:
match = DEFINE_REGEX.match(line)
Comment thread
vstinner marked this conversation as resolved.
Expand All @@ -40,28 +41,22 @@ def parse_file(filename, names, ignored):
print(f"ERROR: {filename}: Unable to parse {line!r}")
sys.exit(1)
name = match.group(1)
if name in ignored:
continue

names.append((name, filename))


def parse_pyconfig_in(filename, names, ignored):
def parse_pyconfig_in(filename, names):
with open(filename, encoding='utf8') as fp:
for line in fp:
match = UNDEF_REGEX.fullmatch(line)
match = UNDEF_REGEX.match(line)
if not match:
continue
name = match.group(1)
if name in ignored:
continue
names.append((name, filename))


def get_ignored_names():
filename = os.path.join(TOOLS_BUILD_DIR, 'check_capi_macros_ignored.txt')
ignored = set()
with open(filename, encoding='utf8') as fp:
with open(IGNORED_FILENAME, encoding='utf8') as fp:
for line in fp:
name = line.strip()
if name.startswith('#'):
Expand All @@ -73,32 +68,53 @@ def get_ignored_names():


def main():
ignored = get_ignored_names()
failure = False

include_dir = os.path.join(SRC_DIR, 'Include')
files = glob.glob(os.path.join(include_dir, '*.h'))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Forgot to post this yesterday)

Include/opcode_ids.h contains generated opcodes, we could exclude these:

Suggested change
files = glob.glob(os.path.join(include_dir, '*.h'))
files = glob.glob(os.path.join(include_dir, '*.h'))
files.remove(os.path.join(include_dir, 'opcode_ids.h'))

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.

files.extend(glob.glob(os.path.join(include_dir, 'cpython', '*.h')))
names = []
names = [] # list of (name: str, filename: str)
for filename in files:
parse_file(filename, names, ignored)
parse_file(filename, names)

filename = os.path.join(SRC_DIR, 'pyconfig.h.in')
parse_file(filename, names, ignored)
parse_pyconfig_in(filename, names, ignored)
parse_file(filename, names)
parse_pyconfig_in(filename, names)
names.sort()

if not names:
names_set = {name for name, filename in names}
ignored = get_ignored_names()
outdated = ignored - names_set
if outdated:
print(f"ERROR: {IGNORED_FILENAME} is outdated, "
"the macros can be removed:")
print()
for name in sorted(outdated):
print(f" - {name}")
Comment thread
vstinner marked this conversation as resolved.
Outdated
print()
print(f"Total: {len(outdated)} macros")
print()
failure = True

new_macros = names_set - ignored
if new_macros:
print('ERROR: the Python C API defines the following macros '
'with a name not starting with "Py":')
print()
count = 0
for name, filename in names:
if name in ignored:
continue
print(f"- {name} defined by {filename}")
count += 1
print()
print(f"Total: {count} macros")

if not failure:
print("OK: the Python C API only defines macros with names "
f"starting with Py (ignoring {len(ignored)} macros)")
sys.exit(0)

print('ERROR: the Python C API defines the following macros '
'with a name not starting with "Py":')
print()
for name, filename in names:
print(f"- {name} defined by {filename}")
print()
print(f"Total: {len(names)} macros")
sys.exit(1)


Expand Down
1 change: 0 additions & 1 deletion Tools/build/check_capi_macros_ignored.txt
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,6 @@ FUTURE_NESTED_SCOPES
FUTURE_PRINT_FUNCTION
FUTURE_UNICODE_LITERALS
FUTURE_WITH_STATEMENT
FVC_ALT_REPR
FVC_ASCII
FVC_MASK
FVC_NONE
Expand Down