https://github.com/python/cpython/commit/978a8befa5ec9b28b161a56b16d326330aef4733
commit: 978a8befa5ec9b28b161a56b16d326330aef4733
branch: main
author: Victor Stinner <[email protected]>
committer: vstinner <[email protected]>
date: 2026-10-06T19:15:14Z
summary:

gh-157695: Parse also _Py_HAVE_xxx variables in sysconfig (#158926)

Add parse_config_h() tests to test_sysconfig.

files:
M Lib/sysconfig/__init__.py
M Lib/test/test_sysconfig.py

diff --git a/Lib/sysconfig/__init__.py b/Lib/sysconfig/__init__.py
index fe55f48647e9a86..0657e458806e9b6 100644
--- a/Lib/sysconfig/__init__.py
+++ b/Lib/sysconfig/__init__.py
@@ -437,9 +437,10 @@ def parse_config_h(fp, vars=None):
     if vars is None:
         vars = {}
     import re
-    define_rx = re.compile("#define ([A-Z][A-Za-z0-9_]+) (.*)\n")
-    undef_rx = re.compile("/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n")
-    quoted_re = re.compile('^"(.*)"$')
+    name_rx = '(?:[A-Z]|_Py_)[A-Za-z0-9_]+'
+    define_rx = re.compile(fr"#define ({name_rx}) (.*)\n")
+    undef_rx = re.compile(fr"/[*] #undef ({name_rx}) [*]/\n")
+    quoted_re = re.compile(r'"(.*)"')
 
     while True:
         line = fp.readline()
@@ -448,7 +449,7 @@ def parse_config_h(fp, vars=None):
         m = define_rx.match(line)
         if m:
             n, v = m.group(1, 2)
-            if mq := quoted_re.match(v):
+            if mq := quoted_re.fullmatch(v):
                 v = mq.group(1)
             try:
                 if n in _ALWAYS_STR:
diff --git a/Lib/test/test_sysconfig.py b/Lib/test/test_sysconfig.py
index 126e314db149165..6b90f2960672b69 100644
--- a/Lib/test/test_sysconfig.py
+++ b/Lib/test/test_sysconfig.py
@@ -29,7 +29,7 @@
                        get_path, get_path_names, _INSTALL_SCHEMES,
                        get_default_scheme, get_scheme_names, get_config_var,
                        _expand_vars, _get_preferred_schemes,
-                       is_python_build, _PROJECT_BASE)
+                       is_python_build, _PROJECT_BASE, parse_config_h)
 from sysconfig.__main__ import _main, _parse_makefile, _get_pybuilddir, 
_get_json_data_name
 import _imp
 import _osx_support
@@ -767,6 +767,72 @@ def test_sysconfig_config_vars_no_prefix_cache(self):
         self.assertEqual(config_vars['exec_prefix'], sys.exec_prefix)
         self.assertEqual(config_vars['platbase'], sys.exec_prefix)
 
+    def test_parse_config_h(self):
+        config = textwrap.dedent('''
+            #ifndef Py_PYCONFIG_H
+            #define Py_PYCONFIG_H
+
+            /* C comment */
+
+            #define ALIGNOF_LONG 8
+            #define HAVE_ACCEPT 1
+            #define _Py_HAVE_COSPI 1
+            #define INVALID_NUMBER abc
+            #define ALT_SOABI "cpython-316t-x86_64-linux-gnu"
+
+            // Undef macros must be written as "/* #undef NAME */":
+            // name must be valid and there is not value.
+            /* #undef ANDROID_API_LEVEL */
+            #undef IGNORE_UNDEF
+            /* #undef IGNORE_VALUE 1 */
+
+            # _ALWAYS_STR: don't convert values to an integer,
+            # but quotes are removed
+            #define IPHONEOS_DEPLOYMENT_TARGET "13.0"
+            #define MACOSX_DEPLOYMENT_TARGET 10
+
+            // Spaces are tolerated after the name, not before
+            #define SPACES_AFTER    1
+            #define    IGNORED_SPACES_BEFORE 1
+
+            // Ignore macro without value
+            #define IGNORE_NO_VALUE
+
+            // Ignore macros with an invalid name
+            #define _PRIVATE_IGNORED 1
+            #define aLOWER_IGNORED 1
+            #define 123IGNORED 1
+            #define INVALID-NAME 1
+            #define INVALID#NAME 1
+            #define NONASCII_NAME_é 1
+
+            // Ignore single letter names
+            #define A 1
+            /* #undef A */
+
+            #endif /*Py_PYCONFIG_H*/
+        ''')
+
+        filename = TESTFN
+        self.addCleanup(unlink, filename)
+        with open(filename, "w", encoding="utf-8") as fp:
+            fp.write(config)
+        vars = {}
+        with open(filename, encoding="utf-8") as fp:
+            parse_config_h(fp, vars)
+        expected = {
+            'ALIGNOF_LONG': 8,
+            'HAVE_ACCEPT': 1,
+            '_Py_HAVE_COSPI': 1,
+            'INVALID_NUMBER': 'abc',
+            'ALT_SOABI': 'cpython-316t-x86_64-linux-gnu',
+            'ANDROID_API_LEVEL': 0,
+            'IPHONEOS_DEPLOYMENT_TARGET': '13.0',
+            'MACOSX_DEPLOYMENT_TARGET': '10',  # str, not int
+            'SPACES_AFTER': 1,
+        }
+        self.assertEqual(vars, expected)
+
 
 class MakefileTests(unittest.TestCase):
 

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to