Skip to content
New issue

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

GH-127178: convert _sysconfigdata to a JSON file #127180

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
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
49 changes: 31 additions & 18 deletions Lib/sysconfig/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ def joinuser(*args):
# True iff _CONFIG_VARS has been fully initialized.
_CONFIG_VARS_INITIALIZED = False
_USER_BASE = None
_SYSCONFIGDATA = None


def _safe_realpath(path):
Expand Down Expand Up @@ -334,27 +335,39 @@ def _get_sysconfigdata_name():
f'_sysconfigdata_{sys.abiflags}_{sys.platform}_{multiarch}',
)


def _get_sysconfigdata():
# _sysconfigdata is generated at build time, see _generate_posix_vars()
global _SYSCONFIGDATA

if _SYSCONFIGDATA is None:
name = _get_sysconfigdata_name() + '.json'
if '_PYTHON_SYSCONFIGDATA_PATH' in os.environ:
data_path = os.path.join(os.environ['_PYTHON_SYSCONFIGDATA_PATH'], name)
else:
# Search sys.path
# FIXME: We should not need this if we could reliably identify the project directory on source builds
for path in sys.path:
data_path = os.path.join(path, name)
if os.path.isfile(data_path):
break
else:
import warnings
warnings.warn(f'Could not find {name}', RuntimeWarning)
return {}

import json

with open(data_path) as f:
_SYSCONFIGDATA = json.load(f)

return _SYSCONFIGDATA


def _init_posix(vars):
"""Initialize the module as appropriate for POSIX systems."""
# _sysconfigdata is generated at build time, see _generate_posix_vars()
name = _get_sysconfigdata_name()

# For cross builds, the path to the target's sysconfigdata must be specified
# so it can be imported. It cannot be in PYTHONPATH, as foreign modules in
# sys.path can cause crashes when loaded by the host interpreter.
# Rely on truthiness as a valueless env variable is still an empty string.
# See OS X note in _generate_posix_vars re _sysconfigdata.
if (path := os.environ.get('_PYTHON_SYSCONFIGDATA_PATH')):
from importlib.machinery import FileFinder, SourceFileLoader, SOURCE_SUFFIXES
from importlib.util import module_from_spec
spec = FileFinder(path, (SourceFileLoader, SOURCE_SUFFIXES)).find_spec(name)
_temp = module_from_spec(spec)
spec.loader.exec_module(_temp)
else:
_temp = __import__(name, globals(), locals(), ['build_time_vars'], 0)
build_time_vars = _temp.build_time_vars
# GH-126920: Make sure we don't overwrite any of the keys already set
vars.update(build_time_vars | vars)
vars.update(_get_sysconfigdata() | vars)

def _init_non_posix(vars):
"""Initialize the module as appropriate for NT"""
Expand Down
19 changes: 6 additions & 13 deletions Lib/sysconfig/__main__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import json
import os
import sys
import sysconfig
from sysconfig import (
_ALWAYS_STR,
_PYTHON_BUILD,
Expand Down Expand Up @@ -192,28 +194,19 @@ def _generate_posix_vars():
# _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.
# for _init_posix() to work, we set the _SYSCONFIGDATA cache directly
name = _get_sysconfigdata_name()
if 'darwin' in sys.platform:
import types
module = types.ModuleType(name)
module.build_time_vars = vars
sys.modules[name] = module
sysconfig._SYSCONFIGDATA = vars

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

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)
json.dump(vars, f, indent=2)

# Create file used for sys.path fixup -- see Modules/getpath.c
with open('pybuilddir.txt', 'w', encoding='utf8') as f:
Expand Down
2 changes: 1 addition & 1 deletion Makefile.pre.in
Original file line number Diff line number Diff line change
Expand Up @@ -2645,7 +2645,7 @@ libinstall: all $(srcdir)/Modules/xxmodule.c
esac; \
done; \
done
$(INSTALL_DATA) `cat pybuilddir.txt`/_sysconfigdata_$(ABIFLAGS)_$(MACHDEP)_$(MULTIARCH).py \
$(INSTALL_DATA) `cat pybuilddir.txt`/_sysconfigdata_$(ABIFLAGS)_$(MACHDEP)_$(MULTIARCH).json \
$(DESTDIR)$(LIBDEST); \
$(INSTALL_DATA) $(srcdir)/LICENSE $(DESTDIR)$(LIBDEST)/LICENSE.txt
@ # If app store compliance has been configured, apply the patch to the
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
The ``_sysconfigdata_*`` module that holds the :mod:`sysconfig` data on
POSIX has been converted to a JSON file.
Loading