diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index 305881a2e591d3..45ab50c47d3bc6 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -199,6 +199,15 @@ always available. Unless explicitly noted otherwise, all variables are read-only .. versionadded:: 3.3 +.. data:: build_prefix + + Set during Python startup, before ``site.py`` is run. If running Python from + source, the value will be set to the build path, otherwise it will be set to + :const:`None`. + + .. versionadded:: 3.12 + + .. data:: byteorder An indicator of the native byte order. This will have the value ``'big'`` on diff --git a/Include/cpython/initconfig.h b/Include/cpython/initconfig.h index 846e05fb26a4d8..920ea3eb95da79 100644 --- a/Include/cpython/initconfig.h +++ b/Include/cpython/initconfig.h @@ -210,6 +210,7 @@ typedef struct PyConfig { wchar_t *base_prefix; wchar_t *exec_prefix; wchar_t *base_exec_prefix; + wchar_t *build_prefix; /* --- Parameter only used by Py_Main() ---------- */ int skip_source_first_line; diff --git a/Lib/sysconfig/__init__.py b/Lib/sysconfig/__init__.py index 0657e458806e9b..20a55174afe05b 100644 --- a/Lib/sysconfig/__init__.py +++ b/Lib/sysconfig/__init__.py @@ -221,13 +221,14 @@ def _safe_realpath(path): if "_PYTHON_PROJECT_BASE" in os.environ: _PROJECT_BASE = _safe_realpath(os.environ["_PYTHON_PROJECT_BASE"]) -def is_python_build(): - for fn in ("Setup", "Setup.local"): - if os.path.isfile(os.path.join(_PROJECT_BASE, "Modules", fn)): - return True - return False - -_PYTHON_BUILD = is_python_build() +def is_python_build(check_home=None): + if check_home is not None: + import warnings + warnings.warn("check_home argument is deprecated and ignored.", + DeprecationWarning, stacklevel=2) + return bool(sys.build_prefix) + +_PYTHON_BUILD = bool(sys.build_prefix) if _PYTHON_BUILD: for scheme in ('posix_prefix', 'posix_home'): diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py index 8ef2bcc3249b8b..562ee69218c4b3 100644 --- a/Lib/test/test_embed.py +++ b/Lib/test/test_embed.py @@ -1099,6 +1099,9 @@ def check_all_configs(self, testname, expected_config=None, except json.JSONDecodeError: self.fail(f"fail to decode stdout: {out!r}") + # build_prefix depends on if Python was installed, ignore it here + configs['config'].pop('build_prefix', None) + self.check_pre_config(configs, expected_preconfig) self.check_config(configs, expected_config) self.check_global_config(configs) diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index c6056154e2d037..f19147519c95fc 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -775,6 +775,7 @@ def test_attributes(self): self.assertIsInstance(sys.platform, str) self.assertIsInstance(sys.prefix, str) self.assertIsInstance(sys.base_prefix, str) + self.assertIsInstance(sys.build_prefix, (str, None)) self.assertIsInstance(sys.platlibdir, str) self.assertIsInstance(sys.version, str) vi = sys.version_info diff --git a/Misc/NEWS.d/next/Library/2022-11-09-03-07-26.gh-issue-98718.rY4eIr.rst b/Misc/NEWS.d/next/Library/2022-11-09-03-07-26.gh-issue-98718.rY4eIr.rst new file mode 100644 index 00000000000000..bd7bd9636c0510 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-11-09-03-07-26.gh-issue-98718.rY4eIr.rst @@ -0,0 +1,2 @@ +:mod:`sys` now exports :attr:`sys.build_prefix`, with the path of the Python +build directory, if any. diff --git a/Modules/getpath.py b/Modules/getpath.py index f5f928f1795de5..d3cc7be3177b3b 100644 --- a/Modules/getpath.py +++ b/Modules/getpath.py @@ -544,6 +544,9 @@ def search_up(prefix, *landmarks, test=isfile): config['_is_python_build'] = 1 +config['build_prefix'] = build_prefix + + # ****************************************************************************** # CALCULATE prefix AND exec_prefix # ****************************************************************************** diff --git a/Python/initconfig.c b/Python/initconfig.c index 225ee0a3fe5ff2..ed357f8a654a4b 100644 --- a/Python/initconfig.c +++ b/Python/initconfig.c @@ -114,6 +114,7 @@ static const PyConfigSpec PYCONFIG_SPEC[] = { SPEC(base_exec_prefix, WSTR_OPT, PUBLIC, SYS_ATTR("base_exec_prefix")), SPEC(base_executable, WSTR_OPT, PUBLIC, SYS_ATTR("_base_executable")), SPEC(base_prefix, WSTR_OPT, PUBLIC, SYS_ATTR("base_prefix")), + SPEC(build_prefix, WSTR_OPT, PUBLIC, SYS_ATTR("build_prefix")), SPEC(bytes_warning, UINT, PUBLIC, SYS_FLAG(9)), SPEC(cpu_count, INT, PUBLIC, NO_SYS), SPEC(lazy_imports, INT, PUBLIC, NO_SYS), @@ -1088,6 +1089,7 @@ PyConfig_Clear(PyConfig *config) CLEAR(config->base_prefix); CLEAR(config->exec_prefix); CLEAR(config->base_exec_prefix); + CLEAR(config->build_prefix); CLEAR(config->platlibdir); CLEAR(config->sys_path_0); @@ -3965,6 +3967,7 @@ _Py_DumpPathConfig(PyThreadState *tstate) DUMP_SYS(_base_executable); DUMP_SYS(base_prefix); DUMP_SYS(base_exec_prefix); + DUMP_SYS(build_prefix); DUMP_SYS(platlibdir); DUMP_SYS(executable); DUMP_SYS(prefix); diff --git a/Python/sysmodule.c b/Python/sysmodule.c index ce75027f67e9f4..4dfaa031fa7d81 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -4202,6 +4202,7 @@ _PySys_UpdateConfig(PyThreadState *tstate) COPY_WSTR("exec_prefix", config->exec_prefix); COPY_WSTR("base_exec_prefix", config->base_exec_prefix); COPY_WSTR("platlibdir", config->platlibdir); + COPY_WSTR("build_prefix", config->build_prefix); if (config->pycache_prefix != NULL) { SET_SYS_FROM_WSTR("pycache_prefix", config->pycache_prefix);