Skip to content

Commit

Permalink
pythonGH-103480: make sysconfig a package (pythonGH-110785)
Browse files Browse the repository at this point in the history
  • Loading branch information
FFY00 authored and Glyphack committed Jan 27, 2024
1 parent 9bcc71d commit 9fd4958
Show file tree
Hide file tree
Showing 4 changed files with 250 additions and 229 deletions.
227 changes: 0 additions & 227 deletions Lib/sysconfig.py → Lib/sysconfig/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,12 +180,6 @@ def joinuser(*args):
_CONFIG_VARS_INITIALIZED = False
_USER_BASE = None

# Regexes needed for parsing Makefile (and similar syntaxes,
# like old-style Setup files).
_variable_rx = r"([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)"
_findvar1_rx = r"\$\(([A-Za-z][A-Za-z0-9_]*)\)"
_findvar2_rx = r"\${([A-Za-z][A-Za-z0-9_]*)}"


def _safe_realpath(path):
try:
Expand Down Expand Up @@ -317,134 +311,6 @@ def get_default_scheme():
return get_preferred_scheme('prefix')


def _parse_makefile(filename, vars=None, keep_unresolved=True):
"""Parse a Makefile-style file.
A dictionary containing name/value pairs is returned. If an
optional dictionary is passed in as the second argument, it is
used instead of a new dictionary.
"""
import re

if vars is None:
vars = {}
done = {}
notdone = {}

with open(filename, encoding=sys.getfilesystemencoding(),
errors="surrogateescape") as f:
lines = f.readlines()

for line in lines:
if line.startswith('#') or line.strip() == '':
continue
m = re.match(_variable_rx, line)
if m:
n, v = m.group(1, 2)
v = v.strip()
# `$$' is a literal `$' in make
tmpv = v.replace('$$', '')

if "$" in tmpv:
notdone[n] = v
else:
try:
if n in _ALWAYS_STR:
raise ValueError

v = int(v)
except ValueError:
# insert literal `$'
done[n] = v.replace('$$', '$')
else:
done[n] = v

# do variable interpolation here
variables = list(notdone.keys())

# Variables with a 'PY_' prefix in the makefile. These need to
# be made available without that prefix through sysconfig.
# Special care is needed to ensure that variable expansion works, even
# if the expansion uses the name without a prefix.
renamed_variables = ('CFLAGS', 'LDFLAGS', 'CPPFLAGS')

while len(variables) > 0:
for name in tuple(variables):
value = notdone[name]
m1 = re.search(_findvar1_rx, value)
m2 = re.search(_findvar2_rx, value)
if m1 and m2:
m = m1 if m1.start() < m2.start() else m2
else:
m = m1 if m1 else m2
if m is not None:
n = m.group(1)
found = True
if n in done:
item = str(done[n])
elif n in notdone:
# get it on a subsequent round
found = False
elif n in os.environ:
# do it like make: fall back to environment
item = os.environ[n]

elif n in renamed_variables:
if (name.startswith('PY_') and
name[3:] in renamed_variables):
item = ""

elif 'PY_' + n in notdone:
found = False

else:
item = str(done['PY_' + n])

else:
done[n] = item = ""

if found:
after = value[m.end():]
value = value[:m.start()] + item + after
if "$" in after:
notdone[name] = value
else:
try:
if name in _ALWAYS_STR:
raise ValueError
value = int(value)
except ValueError:
done[name] = value.strip()
else:
done[name] = value
variables.remove(name)

if name.startswith('PY_') \
and name[3:] in renamed_variables:

name = name[3:]
if name not in done:
done[name] = value

else:
# Adds unresolved variables to the done dict.
# This is disabled when called from distutils.sysconfig
if keep_unresolved:
done[name] = value
# bogus variable reference (e.g. "prefix=$/opt/python");
# just drop it since we can't deal
variables.remove(name)

# strip spurious spaces
for k, v in done.items():
if isinstance(v, str):
done[k] = v.strip()

# save the results in the global dictionary
vars.update(done)
return vars


def get_makefile_filename():
"""Return the path of the Makefile."""
if _PYTHON_BUILD:
Expand All @@ -465,74 +331,6 @@ def _get_sysconfigdata_name():
f'_sysconfigdata_{sys.abiflags}_{sys.platform}_{multiarch}',
)

def _print_config_dict(d, stream):
print ("{", file=stream)
for k, v in sorted(d.items()):
print(f" {k!r}: {v!r},", file=stream)
print ("}", file=stream)

def _generate_posix_vars():
"""Generate the Python module containing build-time variables."""
vars = {}
# load the installed Makefile:
makefile = get_makefile_filename()
try:
_parse_makefile(makefile, vars)
except OSError as e:
msg = f"invalid Python installation: unable to open {makefile}"
if hasattr(e, "strerror"):
msg = f"{msg} ({e.strerror})"
raise OSError(msg)
# load the installed pyconfig.h:
config_h = get_config_h_filename()
try:
with open(config_h, encoding="utf-8") as f:
parse_config_h(f, vars)
except OSError as e:
msg = f"invalid Python installation: unable to open {config_h}"
if hasattr(e, "strerror"):
msg = f"{msg} ({e.strerror})"
raise OSError(msg)
# On AIX, there are wrong paths to the linker scripts in the Makefile
# -- these paths are relative to the Python source, but when installed
# the scripts are in another directory.
if _PYTHON_BUILD:
vars['BLDSHARED'] = vars['LDSHARED']

# There's a chicken-and-egg situation on OS X with regards to the
# _sysconfigdata module after the changes introduced by #15298:
# get_config_vars() is called by get_platform() as part of the
# `make pybuilddir.txt` target -- which is a precursor to the
# _sysconfigdata.py module being constructed. Unfortunately,
# get_config_vars() eventually calls _init_posix(), which attempts
# to import _sysconfigdata, which we won't have built yet. In order
# for _init_posix() to work, if we're on Darwin, just mock up the
# _sysconfigdata module manually and populate it with the build vars.
# This is more than sufficient for ensuring the subsequent call to
# get_platform() succeeds.
name = _get_sysconfigdata_name()
if 'darwin' in sys.platform:
import types
module = types.ModuleType(name)
module.build_time_vars = vars
sys.modules[name] = module

pybuilddir = f'build/lib.{get_platform()}-{_PY_VERSION_SHORT}'
if hasattr(sys, "gettotalrefcount"):
pybuilddir += '-pydebug'
os.makedirs(pybuilddir, exist_ok=True)
destfile = os.path.join(pybuilddir, name + '.py')

with open(destfile, 'w', encoding='utf8') as f:
f.write('# system configuration generated and used by'
' the sysconfig module\n')
f.write('build_time_vars = ')
_print_config_dict(vars, stream=f)

# Create file used for sys.path fixup -- see Modules/getpath.c
with open('pybuilddir.txt', 'w', encoding='utf8') as f:
f.write(pybuilddir)

def _init_posix(vars):
"""Initialize the module as appropriate for POSIX systems."""
# _sysconfigdata is generated at build time, see _generate_posix_vars()
Expand Down Expand Up @@ -857,28 +655,3 @@ def expand_makefile_vars(s, vars):
else:
break
return s


def _print_dict(title, data):
for index, (key, value) in enumerate(sorted(data.items())):
if index == 0:
print(f'{title}: ')
print(f'\t{key} = "{value}"')


def _main():
"""Display all information sysconfig detains."""
if '--generate-posix-vars' in sys.argv:
_generate_posix_vars()
return
print(f'Platform: "{get_platform()}"')
print(f'Python version: "{get_python_version()}"')
print(f'Current installation scheme: "{get_default_scheme()}"')
print()
_print_dict('Paths', get_paths())
print()
_print_dict('Variables', get_config_vars())


if __name__ == '__main__':
_main()
Loading

0 comments on commit 9fd4958

Please sign in to comment.