bpo-40854: Allow overriding sys.platlibdir via PYTHONPLATLIBDIR env-var (GH-20605)

This commit is contained in:
Sandro Mani 2020-06-08 17:28:11 +02:00 committed by GitHub
parent c6b292cdee
commit 8f023a2f66
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 82 additions and 24 deletions

View File

@ -436,6 +436,14 @@ PyConfig
:data:`sys.base_prefix`. :data:`sys.base_prefix`.
.. c:member:: wchar_t* platlibdir
:data:`sys.platlibdir`: platform library directory name, set at configure time
by ``--with-platlibdir``, overrideable by the ``PYTHONPLATLIBDIR``
environment variable.
.. versionadded:: 3.10
.. c:member:: int buffered_stdio .. c:member:: int buffered_stdio
If equals to 0, enable unbuffered mode, making the stdout and stderr If equals to 0, enable unbuffered mode, making the stdout and stderr
@ -884,6 +892,7 @@ Path Configuration
* Path configuration inputs: * Path configuration inputs:
* :c:member:`PyConfig.home` * :c:member:`PyConfig.home`
* :c:member:`PyConfig.platlibdir`
* :c:member:`PyConfig.pathconfig_warnings` * :c:member:`PyConfig.pathconfig_warnings`
* :c:member:`PyConfig.program_name` * :c:member:`PyConfig.program_name`
* :c:member:`PyConfig.pythonpath_env` * :c:member:`PyConfig.pythonpath_env`

View File

@ -538,6 +538,14 @@ conflict.
within a Python program as the variable :data:`sys.path`. within a Python program as the variable :data:`sys.path`.
.. envvar:: PYTHONPLATLIBDIR
If this is set to a non-empty string, it overrides the :data:`sys.platlibdir`
value.
.. versionadded:: 3.10
.. envvar:: PYTHONSTARTUP .. envvar:: PYTHONSTARTUP
If this is the name of a readable file, the Python commands in that file are If this is the name of a readable file, the Python commands in that file are

View File

@ -385,6 +385,7 @@ typedef struct {
wchar_t *base_prefix; /* sys.base_prefix */ wchar_t *base_prefix; /* sys.base_prefix */
wchar_t *exec_prefix; /* sys.exec_prefix */ wchar_t *exec_prefix; /* sys.exec_prefix */
wchar_t *base_exec_prefix; /* sys.base_exec_prefix */ wchar_t *base_exec_prefix; /* sys.base_exec_prefix */
wchar_t *platlibdir; /* sys.platlibdir */
/* --- Parameter only used by Py_Main() ---------- */ /* --- Parameter only used by Py_Main() ---------- */

View File

@ -380,6 +380,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
'exec_prefix': GET_DEFAULT_CONFIG, 'exec_prefix': GET_DEFAULT_CONFIG,
'base_exec_prefix': GET_DEFAULT_CONFIG, 'base_exec_prefix': GET_DEFAULT_CONFIG,
'module_search_paths': GET_DEFAULT_CONFIG, 'module_search_paths': GET_DEFAULT_CONFIG,
'platlibdir': sys.platlibdir,
'site_import': 1, 'site_import': 1,
'bytes_warning': 0, 'bytes_warning': 0,
@ -585,13 +586,14 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
if value is self.GET_DEFAULT_CONFIG: if value is self.GET_DEFAULT_CONFIG:
expected[key] = config[key] expected[key] = config[key]
pythonpath_env = expected['pythonpath_env'] if expected['module_search_paths'] is not self.IGNORE_CONFIG:
if pythonpath_env is not None: pythonpath_env = expected['pythonpath_env']
paths = pythonpath_env.split(os.path.pathsep) if pythonpath_env is not None:
expected['module_search_paths'] = [*paths, *expected['module_search_paths']] paths = pythonpath_env.split(os.path.pathsep)
if modify_path_cb is not None: expected['module_search_paths'] = [*paths, *expected['module_search_paths']]
expected['module_search_paths'] = expected['module_search_paths'].copy() if modify_path_cb is not None:
modify_path_cb(expected['module_search_paths']) expected['module_search_paths'] = expected['module_search_paths'].copy()
modify_path_cb(expected['module_search_paths'])
for key in self.COPY_PRE_CONFIG: for key in self.COPY_PRE_CONFIG:
if key not in expected_preconfig: if key not in expected_preconfig:
@ -764,6 +766,8 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
'buffered_stdio': 0, 'buffered_stdio': 0,
'user_site_directory': 0, 'user_site_directory': 0,
'faulthandler': 1, 'faulthandler': 1,
'platlibdir': 'my_platlibdir',
'module_search_paths': self.IGNORE_CONFIG,
'check_hash_pycs_mode': 'always', 'check_hash_pycs_mode': 'always',
'pathconfig_warnings': 0, 'pathconfig_warnings': 0,
@ -795,6 +799,8 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
'user_site_directory': 0, 'user_site_directory': 0,
'faulthandler': 1, 'faulthandler': 1,
'warnoptions': ['EnvVar'], 'warnoptions': ['EnvVar'],
'platlibdir': 'env_platlibdir',
'module_search_paths': self.IGNORE_CONFIG,
'_use_peg_parser': 0, '_use_peg_parser': 0,
} }
self.check_all_configs("test_init_compat_env", config, preconfig, self.check_all_configs("test_init_compat_env", config, preconfig,
@ -823,6 +829,8 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
'user_site_directory': 0, 'user_site_directory': 0,
'faulthandler': 1, 'faulthandler': 1,
'warnoptions': ['EnvVar'], 'warnoptions': ['EnvVar'],
'platlibdir': 'env_platlibdir',
'module_search_paths': self.IGNORE_CONFIG,
'_use_peg_parser': 0, '_use_peg_parser': 0,
} }
self.check_all_configs("test_init_python_env", config, preconfig, self.check_all_configs("test_init_python_env", config, preconfig,

View File

@ -811,6 +811,11 @@ Python/sysmodule.o: $(srcdir)/Python/sysmodule.c Makefile $(srcdir)/Include/pydt
$(MULTIARCH_CPPFLAGS) \ $(MULTIARCH_CPPFLAGS) \
-o $@ $(srcdir)/Python/sysmodule.c -o $@ $(srcdir)/Python/sysmodule.c
Python/initconfig.o: $(srcdir)/Python/initconfig.c
$(CC) -c $(PY_CORE_CFLAGS) \
-DPLATLIBDIR='"$(PLATLIBDIR)"' \
-o $@ $(srcdir)/Python/initconfig.c
$(IO_OBJS): $(IO_H) $(IO_OBJS): $(IO_H)
.PHONY: regen-grammar .PHONY: regen-grammar

View File

@ -0,0 +1 @@
Allow overriding :data:`sys.platlibdir` via a new :envvar:`PYTHONPLATLIBDIR` environment variable.

View File

@ -413,6 +413,8 @@ inserted in the path in front of $PYTHONPATH.
The search path can be manipulated from within a Python program as the The search path can be manipulated from within a Python program as the
variable variable
.IR sys.path . .IR sys.path .
.IP PYTHONPLATLIBDIR
Override sys.platlibdir.
.IP PYTHONSTARTUP .IP PYTHONSTARTUP
If this is the name of a readable file, the Python commands in that If this is the name of a readable file, the Python commands in that
file are executed before the first prompt is displayed in interactive file are executed before the first prompt is displayed in interactive

View File

@ -105,8 +105,8 @@ extern "C" {
#if (!defined(PREFIX) || !defined(EXEC_PREFIX) \ #if (!defined(PREFIX) || !defined(EXEC_PREFIX) \
|| !defined(VERSION) || !defined(VPATH) || !defined(PLATLIBDIR)) || !defined(VERSION) || !defined(VPATH))
#error "PREFIX, EXEC_PREFIX, VERSION, VPATH and PLATLIBDIR macros must be defined" #error "PREFIX, EXEC_PREFIX, VERSION and VPATH macros must be defined"
#endif #endif
#ifndef LANDMARK #ifndef LANDMARK
@ -128,7 +128,6 @@ typedef struct {
wchar_t *pythonpath_macro; /* PYTHONPATH macro */ wchar_t *pythonpath_macro; /* PYTHONPATH macro */
wchar_t *prefix_macro; /* PREFIX macro */ wchar_t *prefix_macro; /* PREFIX macro */
wchar_t *exec_prefix_macro; /* EXEC_PREFIX macro */ wchar_t *exec_prefix_macro; /* EXEC_PREFIX macro */
wchar_t *platlibdir_macro; /* PLATLIBDIR macro */
wchar_t *vpath_macro; /* VPATH macro */ wchar_t *vpath_macro; /* VPATH macro */
wchar_t *lib_python; /* "lib/pythonX.Y" */ wchar_t *lib_python; /* "lib/pythonX.Y" */
@ -138,6 +137,7 @@ typedef struct {
int warnings; int warnings;
const wchar_t *pythonpath_env; const wchar_t *pythonpath_env;
const wchar_t *platlibdir;
wchar_t *argv0_path; wchar_t *argv0_path;
wchar_t *zip_path; wchar_t *zip_path;
@ -811,7 +811,7 @@ calculate_exec_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig)
} }
/* <PLATLIBDIR> / "lib-dynload" */ /* <PLATLIBDIR> / "lib-dynload" */
wchar_t *lib_dynload = joinpath2(calculate->platlibdir_macro, wchar_t *lib_dynload = joinpath2(calculate->platlibdir,
L"lib-dynload"); L"lib-dynload");
if (lib_dynload == NULL) { if (lib_dynload == NULL) {
return _PyStatus_NO_MEMORY(); return _PyStatus_NO_MEMORY();
@ -1297,7 +1297,7 @@ calculate_zip_path(PyCalculatePath *calculate)
PyStatus res; PyStatus res;
/* Path: <PLATLIBDIR> / "pythonXY.zip" */ /* Path: <PLATLIBDIR> / "pythonXY.zip" */
wchar_t *path = joinpath2(calculate->platlibdir_macro, L"python" Py_STRINGIFY(PY_MAJOR_VERSION) Py_STRINGIFY(PY_MINOR_VERSION) L".zip"); wchar_t *path = joinpath2(calculate->platlibdir, L"python" Py_STRINGIFY(PY_MAJOR_VERSION) Py_STRINGIFY(PY_MINOR_VERSION) L".zip");
if (path == NULL) { if (path == NULL) {
return _PyStatus_NO_MEMORY(); return _PyStatus_NO_MEMORY();
} }
@ -1451,10 +1451,6 @@ calculate_init(PyCalculatePath *calculate, const PyConfig *config)
if (!calculate->vpath_macro) { if (!calculate->vpath_macro) {
return DECODE_LOCALE_ERR("VPATH macro", len); return DECODE_LOCALE_ERR("VPATH macro", len);
} }
calculate->platlibdir_macro = Py_DecodeLocale(PLATLIBDIR, &len);
if (!calculate->platlibdir_macro) {
return DECODE_LOCALE_ERR("PLATLIBDIR macro", len);
}
calculate->lib_python = Py_DecodeLocale(PLATLIBDIR "/python" VERSION, &len); calculate->lib_python = Py_DecodeLocale(PLATLIBDIR "/python" VERSION, &len);
if (!calculate->lib_python) { if (!calculate->lib_python) {
@ -1463,6 +1459,7 @@ calculate_init(PyCalculatePath *calculate, const PyConfig *config)
calculate->warnings = config->pathconfig_warnings; calculate->warnings = config->pathconfig_warnings;
calculate->pythonpath_env = config->pythonpath_env; calculate->pythonpath_env = config->pythonpath_env;
calculate->platlibdir = config->platlibdir;
return _PyStatus_OK(); return _PyStatus_OK();
} }
@ -1475,7 +1472,6 @@ calculate_free(PyCalculatePath *calculate)
PyMem_RawFree(calculate->prefix_macro); PyMem_RawFree(calculate->prefix_macro);
PyMem_RawFree(calculate->exec_prefix_macro); PyMem_RawFree(calculate->exec_prefix_macro);
PyMem_RawFree(calculate->vpath_macro); PyMem_RawFree(calculate->vpath_macro);
PyMem_RawFree(calculate->platlibdir_macro);
PyMem_RawFree(calculate->lib_python); PyMem_RawFree(calculate->lib_python);
PyMem_RawFree(calculate->path_env); PyMem_RawFree(calculate->path_env);
PyMem_RawFree(calculate->zip_path); PyMem_RawFree(calculate->zip_path);

View File

@ -548,6 +548,13 @@ static int test_init_from_config(void)
/* FIXME: test home */ /* FIXME: test home */
/* FIXME: test path config: module_search_path .. dll_path */ /* FIXME: test path config: module_search_path .. dll_path */
putenv("PYTHONPLATLIBDIR=env_platlibdir");
status = PyConfig_SetBytesString(&config, &config.platlibdir, "my_platlibdir");
if (PyStatus_Exception(status)) {
PyConfig_Clear(&config);
Py_ExitStatusException(status);
}
putenv("PYTHONVERBOSE=0"); putenv("PYTHONVERBOSE=0");
Py_VerboseFlag = 0; Py_VerboseFlag = 0;
config.verbose = 1; config.verbose = 1;
@ -668,6 +675,7 @@ static void set_most_env_vars(void)
putenv("PYTHONFAULTHANDLER=1"); putenv("PYTHONFAULTHANDLER=1");
putenv("PYTHONIOENCODING=iso8859-1:replace"); putenv("PYTHONIOENCODING=iso8859-1:replace");
putenv("PYTHONOLDPARSER=1"); putenv("PYTHONOLDPARSER=1");
putenv("PYTHONPLATLIBDIR=env_platlibdir");
} }

View File

@ -24,6 +24,10 @@
# endif # endif
#endif #endif
#ifndef PLATLIBDIR
# error "PLATLIBDIR macro must be defined"
#endif
/* --- Command line options --------------------------------------- */ /* --- Command line options --------------------------------------- */
@ -110,6 +114,7 @@ PYTHONPATH : '%lc'-separated list of directories prefixed to the\n\
static const char usage_5[] = static const char usage_5[] =
"PYTHONHOME : alternate <prefix> directory (or <prefix>%lc<exec_prefix>).\n" "PYTHONHOME : alternate <prefix> directory (or <prefix>%lc<exec_prefix>).\n"
" The default module search path uses %s.\n" " The default module search path uses %s.\n"
"PYTHONPLATLIBDIR : override sys.platlibdir.\n"
"PYTHONCASEOK : ignore case in 'import' statements (Windows).\n" "PYTHONCASEOK : ignore case in 'import' statements (Windows).\n"
"PYTHONUTF8: if set to 1, enable the UTF-8 mode.\n" "PYTHONUTF8: if set to 1, enable the UTF-8 mode.\n"
"PYTHONIOENCODING: Encoding[:errors] used for stdin/stdout/stderr.\n" "PYTHONIOENCODING: Encoding[:errors] used for stdin/stdout/stderr.\n"
@ -588,6 +593,7 @@ PyConfig_Clear(PyConfig *config)
CLEAR(config->base_prefix); CLEAR(config->base_prefix);
CLEAR(config->exec_prefix); CLEAR(config->exec_prefix);
CLEAR(config->base_exec_prefix); CLEAR(config->base_exec_prefix);
CLEAR(config->platlibdir);
CLEAR(config->filesystem_encoding); CLEAR(config->filesystem_encoding);
CLEAR(config->filesystem_errors); CLEAR(config->filesystem_errors);
@ -824,6 +830,7 @@ _PyConfig_Copy(PyConfig *config, const PyConfig *config2)
COPY_WSTR_ATTR(base_prefix); COPY_WSTR_ATTR(base_prefix);
COPY_WSTR_ATTR(exec_prefix); COPY_WSTR_ATTR(exec_prefix);
COPY_WSTR_ATTR(base_exec_prefix); COPY_WSTR_ATTR(base_exec_prefix);
COPY_WSTR_ATTR(platlibdir);
COPY_ATTR(site_import); COPY_ATTR(site_import);
COPY_ATTR(bytes_warning); COPY_ATTR(bytes_warning);
@ -926,6 +933,7 @@ config_as_dict(const PyConfig *config)
SET_ITEM_WSTR(base_prefix); SET_ITEM_WSTR(base_prefix);
SET_ITEM_WSTR(exec_prefix); SET_ITEM_WSTR(exec_prefix);
SET_ITEM_WSTR(base_exec_prefix); SET_ITEM_WSTR(base_exec_prefix);
SET_ITEM_WSTR(platlibdir);
SET_ITEM_INT(site_import); SET_ITEM_INT(site_import);
SET_ITEM_INT(bytes_warning); SET_ITEM_INT(bytes_warning);
SET_ITEM_INT(inspect); SET_ITEM_INT(inspect);
@ -1336,6 +1344,14 @@ config_read_env_vars(PyConfig *config)
} }
} }
if(config->platlibdir == NULL) {
status = CONFIG_GET_ENV_DUP(config, &config->platlibdir,
L"PYTHONPLATLIBDIR", "PYTHONPLATLIBDIR");
if (_PyStatus_EXCEPTION(status)) {
return status;
}
}
if (config->use_hash_seed < 0) { if (config->use_hash_seed < 0) {
status = config_init_hash_seed(config); status = config_init_hash_seed(config);
if (_PyStatus_EXCEPTION(status)) { if (_PyStatus_EXCEPTION(status)) {
@ -1731,6 +1747,14 @@ config_read(PyConfig *config)
} }
} }
if(config->platlibdir == NULL) {
status = CONFIG_SET_BYTES_STR(config, &config->platlibdir, PLATLIBDIR,
"PLATLIBDIR macro");
if (_PyStatus_EXCEPTION(status)) {
return status;
}
}
if (config->_install_importlib) { if (config->_install_importlib) {
status = _PyConfig_InitPathConfig(config); status = _PyConfig_InitPathConfig(config);
if (_PyStatus_EXCEPTION(status)) { if (_PyStatus_EXCEPTION(status)) {
@ -2554,6 +2578,7 @@ PyConfig_Read(PyConfig *config)
assert(config->exec_prefix != NULL); assert(config->exec_prefix != NULL);
assert(config->base_exec_prefix != NULL); assert(config->base_exec_prefix != NULL);
} }
assert(config->platlibdir != NULL);
assert(config->filesystem_encoding != NULL); assert(config->filesystem_encoding != NULL);
assert(config->filesystem_errors != NULL); assert(config->filesystem_errors != NULL);
assert(config->stdio_encoding != NULL); assert(config->stdio_encoding != NULL);
@ -2704,6 +2729,7 @@ _Py_DumpPathConfig(PyThreadState *tstate)
DUMP_SYS(_base_executable); DUMP_SYS(_base_executable);
DUMP_SYS(base_prefix); DUMP_SYS(base_prefix);
DUMP_SYS(base_exec_prefix); DUMP_SYS(base_exec_prefix);
DUMP_SYS(platlibdir);
DUMP_SYS(executable); DUMP_SYS(executable);
DUMP_SYS(prefix); DUMP_SYS(prefix);
DUMP_SYS(exec_prefix); DUMP_SYS(exec_prefix);

View File

@ -2922,13 +2922,7 @@ _PySys_InitMain(PyThreadState *tstate)
SET_SYS_FROM_WSTR("base_prefix", config->base_prefix); SET_SYS_FROM_WSTR("base_prefix", config->base_prefix);
SET_SYS_FROM_WSTR("exec_prefix", config->exec_prefix); SET_SYS_FROM_WSTR("exec_prefix", config->exec_prefix);
SET_SYS_FROM_WSTR("base_exec_prefix", config->base_exec_prefix); SET_SYS_FROM_WSTR("base_exec_prefix", config->base_exec_prefix);
{ SET_SYS_FROM_WSTR("platlibdir", config->platlibdir);
PyObject *str = PyUnicode_FromString(PLATLIBDIR);
if (str == NULL) {
return -1;
}
SET_SYS_FROM_STRING("platlibdir", str);
}
if (config->pycache_prefix != NULL) { if (config->pycache_prefix != NULL) {
SET_SYS_FROM_WSTR("pycache_prefix", config->pycache_prefix); SET_SYS_FROM_WSTR("pycache_prefix", config->pycache_prefix);