Skip to content

Commit ece561e

Browse files
committed
gh-157695: Parse also _Py_HAVE_xxx variables in sysconfig (#158926)
Add parse_config_h() tests to test_sysconfig. (cherry picked from commit 978a8be)
1 parent 9461bea commit ece561e

2 files changed

Lines changed: 72 additions & 5 deletions

File tree

‎Lib/sysconfig/__init__.py‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -435,9 +435,10 @@ def parse_config_h(fp, vars=None):
435435
if vars is None:
436436
vars = {}
437437
import re
438-
define_rx = re.compile("#define ([A-Z][A-Za-z0-9_]+) (.*)\n")
439-
undef_rx = re.compile("/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n")
440-
quoted_re = re.compile('^"(.*)"$')
438+
name_rx = '(?:[A-Z]|_Py_)[A-Za-z0-9_]+'
439+
define_rx = re.compile(fr"#define ({name_rx}) (.*)\n")
440+
undef_rx = re.compile(fr"/[*] #undef ({name_rx}) [*]/\n")
441+
quoted_re = re.compile(r'"(.*)"')
441442

442443
while True:
443444
line = fp.readline()
@@ -446,7 +447,7 @@ def parse_config_h(fp, vars=None):
446447
m = define_rx.match(line)
447448
if m:
448449
n, v = m.group(1, 2)
449-
if mq := quoted_re.match(v):
450+
if mq := quoted_re.fullmatch(v):
450451
v = mq.group(1)
451452
try:
452453
if n in _ALWAYS_STR:

‎Lib/test/test_sysconfig.py‎

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
get_path, get_path_names, _INSTALL_SCHEMES,
3030
get_default_scheme, get_scheme_names, get_config_var,
3131
_expand_vars, _get_preferred_schemes,
32-
is_python_build, _PROJECT_BASE)
32+
is_python_build, _PROJECT_BASE, parse_config_h)
3333
from sysconfig.__main__ import _main, _parse_makefile, _get_pybuilddir, _get_json_data_name
3434
import _imp
3535
import _osx_support
@@ -760,6 +760,72 @@ def test_sysconfig_config_vars_no_prefix_cache(self):
760760
self.assertEqual(config_vars['exec_prefix'], sys.exec_prefix)
761761
self.assertEqual(config_vars['platbase'], sys.exec_prefix)
762762

763+
def test_parse_config_h(self):
764+
config = textwrap.dedent('''
765+
#ifndef Py_PYCONFIG_H
766+
#define Py_PYCONFIG_H
767+
768+
/* C comment */
769+
770+
#define ALIGNOF_LONG 8
771+
#define HAVE_ACCEPT 1
772+
#define _Py_HAVE_COSPI 1
773+
#define INVALID_NUMBER abc
774+
#define ALT_SOABI "cpython-316t-x86_64-linux-gnu"
775+
776+
// Undef macros must be written as "/* #undef NAME */":
777+
// name must be valid and there is not value.
778+
/* #undef ANDROID_API_LEVEL */
779+
#undef IGNORE_UNDEF
780+
/* #undef IGNORE_VALUE 1 */
781+
782+
# _ALWAYS_STR: don't convert values to an integer,
783+
# but quotes are removed
784+
#define IPHONEOS_DEPLOYMENT_TARGET "13.0"
785+
#define MACOSX_DEPLOYMENT_TARGET 10
786+
787+
// Spaces are tolerated after the name, not before
788+
#define SPACES_AFTER 1
789+
#define IGNORED_SPACES_BEFORE 1
790+
791+
// Ignore macro without value
792+
#define IGNORE_NO_VALUE
793+
794+
// Ignore macros with an invalid name
795+
#define _PRIVATE_IGNORED 1
796+
#define aLOWER_IGNORED 1
797+
#define 123IGNORED 1
798+
#define INVALID-NAME 1
799+
#define INVALID#NAME 1
800+
#define NONASCII_NAME_é 1
801+
802+
// Ignore single letter names
803+
#define A 1
804+
/* #undef A */
805+
806+
#endif /*Py_PYCONFIG_H*/
807+
''')
808+
809+
filename = TESTFN
810+
self.addCleanup(unlink, filename)
811+
with open(filename, "w", encoding="utf-8") as fp:
812+
fp.write(config)
813+
vars = {}
814+
with open(filename, encoding="utf-8") as fp:
815+
parse_config_h(fp, vars)
816+
expected = {
817+
'ALIGNOF_LONG': 8,
818+
'HAVE_ACCEPT': 1,
819+
'_Py_HAVE_COSPI': 1,
820+
'INVALID_NUMBER': 'abc',
821+
'ALT_SOABI': 'cpython-316t-x86_64-linux-gnu',
822+
'ANDROID_API_LEVEL': 0,
823+
'IPHONEOS_DEPLOYMENT_TARGET': '13.0',
824+
'MACOSX_DEPLOYMENT_TARGET': '10', # str, not int
825+
'SPACES_AFTER': 1,
826+
}
827+
self.assertEqual(vars, expected)
828+
763829

764830
class MakefileTests(unittest.TestCase):
765831

0 commit comments

Comments
 (0)