Skip to content

Commit 8175064

Browse files
vstinnermanisandro
andauthored
bpo-40854: Allow overriding sys.platlibdir via PYTHONPLATLIBDIR env-var (GH-20605) (GH-20725)
(cherry picked from commit 8f023a2) Co-authored-by: Sandro Mani <[email protected]>
1 parent 298c8c8 commit 8175064

File tree

11 files changed

+83
-25
lines changed

11 files changed

+83
-25
lines changed

Doc/c-api/init_config.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,14 @@ PyConfig
437437
438438
:data:`sys.base_prefix`.
439439
440+
.. c:member:: wchar_t* platlibdir
441+
442+
:data:`sys.platlibdir`: platform library directory name, set at configure time
443+
by ``--with-platlibdir``, overrideable by the ``PYTHONPLATLIBDIR``
444+
environment variable.
445+
446+
.. versionadded:: 3.9
447+
440448
.. c:member:: int buffered_stdio
441449
442450
If equals to 0, enable unbuffered mode, making the stdout and stderr
@@ -885,6 +893,7 @@ Path Configuration
885893
* Path configuration inputs:
886894
887895
* :c:member:`PyConfig.home`
896+
* :c:member:`PyConfig.platlibdir`
888897
* :c:member:`PyConfig.pathconfig_warnings`
889898
* :c:member:`PyConfig.program_name`
890899
* :c:member:`PyConfig.pythonpath_env`

Doc/using/cmdline.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,14 @@ conflict.
538538
within a Python program as the variable :data:`sys.path`.
539539

540540

541+
.. envvar:: PYTHONPLATLIBDIR
542+
543+
If this is set to a non-empty string, it overrides the :data:`sys.platlibdir`
544+
value.
545+
546+
.. versionadded:: 3.9
547+
548+
541549
.. envvar:: PYTHONSTARTUP
542550

543551
If this is the name of a readable file, the Python commands in that file are

Include/cpython/initconfig.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,7 @@ typedef struct {
388388
wchar_t *base_prefix; /* sys.base_prefix */
389389
wchar_t *exec_prefix; /* sys.exec_prefix */
390390
wchar_t *base_exec_prefix; /* sys.base_exec_prefix */
391+
wchar_t *platlibdir; /* sys.platlibdir */
391392

392393
/* --- Parameter only used by Py_Main() ---------- */
393394

Lib/test/test_embed.py

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
381381
'exec_prefix': GET_DEFAULT_CONFIG,
382382
'base_exec_prefix': GET_DEFAULT_CONFIG,
383383
'module_search_paths': GET_DEFAULT_CONFIG,
384+
'platlibdir': sys.platlibdir,
384385

385386
'site_import': 1,
386387
'bytes_warning': 0,
@@ -586,13 +587,14 @@ def get_expected_config(self, expected_preconfig, expected, env, api,
586587
if value is self.GET_DEFAULT_CONFIG:
587588
expected[key] = config[key]
588589

589-
pythonpath_env = expected['pythonpath_env']
590-
if pythonpath_env is not None:
591-
paths = pythonpath_env.split(os.path.pathsep)
592-
expected['module_search_paths'] = [*paths, *expected['module_search_paths']]
593-
if modify_path_cb is not None:
594-
expected['module_search_paths'] = expected['module_search_paths'].copy()
595-
modify_path_cb(expected['module_search_paths'])
590+
if expected['module_search_paths'] is not self.IGNORE_CONFIG:
591+
pythonpath_env = expected['pythonpath_env']
592+
if pythonpath_env is not None:
593+
paths = pythonpath_env.split(os.path.pathsep)
594+
expected['module_search_paths'] = [*paths, *expected['module_search_paths']]
595+
if modify_path_cb is not None:
596+
expected['module_search_paths'] = expected['module_search_paths'].copy()
597+
modify_path_cb(expected['module_search_paths'])
596598

597599
for key in self.COPY_PRE_CONFIG:
598600
if key not in expected_preconfig:
@@ -770,6 +772,8 @@ def test_init_from_config(self):
770772
'buffered_stdio': 0,
771773
'user_site_directory': 0,
772774
'faulthandler': 1,
775+
'platlibdir': 'my_platlibdir',
776+
'module_search_paths': self.IGNORE_CONFIG,
773777

774778
'check_hash_pycs_mode': 'always',
775779
'pathconfig_warnings': 0,
@@ -801,6 +805,8 @@ def test_init_compat_env(self):
801805
'user_site_directory': 0,
802806
'faulthandler': 1,
803807
'warnoptions': ['EnvVar'],
808+
'platlibdir': 'env_platlibdir',
809+
'module_search_paths': self.IGNORE_CONFIG,
804810
'_use_peg_parser': 0,
805811
}
806812
self.check_all_configs("test_init_compat_env", config, preconfig,
@@ -829,6 +835,8 @@ def test_init_python_env(self):
829835
'user_site_directory': 0,
830836
'faulthandler': 1,
831837
'warnoptions': ['EnvVar'],
838+
'platlibdir': 'env_platlibdir',
839+
'module_search_paths': self.IGNORE_CONFIG,
832840
'_use_peg_parser': 0,
833841
}
834842
self.check_all_configs("test_init_python_env", config, preconfig,

Makefile.pre.in

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -811,6 +811,11 @@ Python/sysmodule.o: $(srcdir)/Python/sysmodule.c Makefile $(srcdir)/Include/pydt
811811
$(MULTIARCH_CPPFLAGS) \
812812
-o $@ $(srcdir)/Python/sysmodule.c
813813

814+
Python/initconfig.o: $(srcdir)/Python/initconfig.c
815+
$(CC) -c $(PY_CORE_CFLAGS) \
816+
-DPLATLIBDIR='"$(PLATLIBDIR)"' \
817+
-o $@ $(srcdir)/Python/initconfig.c
818+
814819
$(IO_OBJS): $(IO_H)
815820

816821
.PHONY: regen-grammar
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Allow overriding :data:`sys.platlibdir` via a new :envvar:`PYTHONPLATLIBDIR` environment variable.

Misc/python.man

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,8 @@ inserted in the path in front of $PYTHONPATH.
413413
The search path can be manipulated from within a Python program as the
414414
variable
415415
.IR sys.path .
416+
.IP PYTHONPLATLIBDIR
417+
Override sys.platlibdir.
416418
.IP PYTHONSTARTUP
417419
If this is the name of a readable file, the Python commands in that
418420
file are executed before the first prompt is displayed in interactive

Modules/getpath.c

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,8 @@ extern "C" {
105105

106106

107107
#if (!defined(PREFIX) || !defined(EXEC_PREFIX) \
108-
|| !defined(VERSION) || !defined(VPATH) || !defined(PLATLIBDIR))
109-
#error "PREFIX, EXEC_PREFIX, VERSION, VPATH and PLATLIBDIR macros must be defined"
108+
|| !defined(VERSION) || !defined(VPATH))
109+
#error "PREFIX, EXEC_PREFIX, VERSION and VPATH macros must be defined"
110110
#endif
111111

112112
#ifndef LANDMARK
@@ -128,7 +128,6 @@ typedef struct {
128128
wchar_t *pythonpath_macro; /* PYTHONPATH macro */
129129
wchar_t *prefix_macro; /* PREFIX macro */
130130
wchar_t *exec_prefix_macro; /* EXEC_PREFIX macro */
131-
wchar_t *platlibdir_macro; /* PLATLIBDIR macro */
132131
wchar_t *vpath_macro; /* VPATH macro */
133132

134133
wchar_t *lib_python; /* "lib/pythonX.Y" */
@@ -138,6 +137,7 @@ typedef struct {
138137

139138
int warnings;
140139
const wchar_t *pythonpath_env;
140+
const wchar_t *platlibdir;
141141

142142
wchar_t *argv0_path;
143143
wchar_t *zip_path;
@@ -811,7 +811,7 @@ calculate_exec_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig)
811811
}
812812

813813
/* <PLATLIBDIR> / "lib-dynload" */
814-
wchar_t *lib_dynload = joinpath2(calculate->platlibdir_macro,
814+
wchar_t *lib_dynload = joinpath2(calculate->platlibdir,
815815
L"lib-dynload");
816816
if (lib_dynload == NULL) {
817817
return _PyStatus_NO_MEMORY();
@@ -1296,8 +1296,8 @@ calculate_zip_path(PyCalculatePath *calculate)
12961296
{
12971297
PyStatus res;
12981298

1299-
/* Path: <PLATLIBDIR> / "python00.zip" */
1300-
wchar_t *path = joinpath2(calculate->platlibdir_macro, L"python00.zip");
1299+
/* Path: <PLATLIBDIR> / "pythonXY.zip" */
1300+
wchar_t *path = joinpath2(calculate->platlibdir, L"python" Py_STRINGIFY(PY_MAJOR_VERSION) Py_STRINGIFY(PY_MINOR_VERSION) L".zip");
13011301
if (path == NULL) {
13021302
return _PyStatus_NO_MEMORY();
13031303
}
@@ -1456,10 +1456,6 @@ calculate_init(PyCalculatePath *calculate, const PyConfig *config)
14561456
if (!calculate->vpath_macro) {
14571457
return DECODE_LOCALE_ERR("VPATH macro", len);
14581458
}
1459-
calculate->platlibdir_macro = Py_DecodeLocale(PLATLIBDIR, &len);
1460-
if (!calculate->platlibdir_macro) {
1461-
return DECODE_LOCALE_ERR("PLATLIBDIR macro", len);
1462-
}
14631459

14641460
calculate->lib_python = Py_DecodeLocale(PLATLIBDIR "/python" VERSION, &len);
14651461
if (!calculate->lib_python) {
@@ -1468,6 +1464,7 @@ calculate_init(PyCalculatePath *calculate, const PyConfig *config)
14681464

14691465
calculate->warnings = config->pathconfig_warnings;
14701466
calculate->pythonpath_env = config->pythonpath_env;
1467+
calculate->platlibdir = config->platlibdir;
14711468

14721469
return _PyStatus_OK();
14731470
}
@@ -1480,7 +1477,6 @@ calculate_free(PyCalculatePath *calculate)
14801477
PyMem_RawFree(calculate->prefix_macro);
14811478
PyMem_RawFree(calculate->exec_prefix_macro);
14821479
PyMem_RawFree(calculate->vpath_macro);
1483-
PyMem_RawFree(calculate->platlibdir_macro);
14841480
PyMem_RawFree(calculate->lib_python);
14851481
PyMem_RawFree(calculate->path_env);
14861482
PyMem_RawFree(calculate->zip_path);

Programs/_testembed.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,13 @@ static int test_init_from_config(void)
548548
/* FIXME: test home */
549549
/* FIXME: test path config: module_search_path .. dll_path */
550550

551+
putenv("PYTHONPLATLIBDIR=env_platlibdir");
552+
status = PyConfig_SetBytesString(&config, &config.platlibdir, "my_platlibdir");
553+
if (PyStatus_Exception(status)) {
554+
PyConfig_Clear(&config);
555+
Py_ExitStatusException(status);
556+
}
557+
551558
putenv("PYTHONVERBOSE=0");
552559
Py_VerboseFlag = 0;
553560
config.verbose = 1;
@@ -668,6 +675,7 @@ static void set_most_env_vars(void)
668675
putenv("PYTHONFAULTHANDLER=1");
669676
putenv("PYTHONIOENCODING=iso8859-1:replace");
670677
putenv("PYTHONOLDPARSER=1");
678+
putenv("PYTHONPLATLIBDIR=env_platlibdir");
671679
}
672680

673681

Python/initconfig.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@
2424
# endif
2525
#endif
2626

27+
#ifndef PLATLIBDIR
28+
# error "PLATLIBDIR macro must be defined"
29+
#endif
30+
2731

2832
/* --- Command line options --------------------------------------- */
2933

@@ -110,6 +114,7 @@ PYTHONPATH : '%lc'-separated list of directories prefixed to the\n\
110114
static const char usage_5[] =
111115
"PYTHONHOME : alternate <prefix> directory (or <prefix>%lc<exec_prefix>).\n"
112116
" The default module search path uses %s.\n"
117+
"PYTHONPLATLIBDIR : override sys.platlibdir.\n"
113118
"PYTHONCASEOK : ignore case in 'import' statements (Windows).\n"
114119
"PYTHONUTF8: if set to 1, enable the UTF-8 mode.\n"
115120
"PYTHONIOENCODING: Encoding[:errors] used for stdin/stdout/stderr.\n"
@@ -586,6 +591,7 @@ PyConfig_Clear(PyConfig *config)
586591
CLEAR(config->base_prefix);
587592
CLEAR(config->exec_prefix);
588593
CLEAR(config->base_exec_prefix);
594+
CLEAR(config->platlibdir);
589595

590596
CLEAR(config->filesystem_encoding);
591597
CLEAR(config->filesystem_errors);
@@ -822,6 +828,7 @@ _PyConfig_Copy(PyConfig *config, const PyConfig *config2)
822828
COPY_WSTR_ATTR(base_prefix);
823829
COPY_WSTR_ATTR(exec_prefix);
824830
COPY_WSTR_ATTR(base_exec_prefix);
831+
COPY_WSTR_ATTR(platlibdir);
825832

826833
COPY_ATTR(site_import);
827834
COPY_ATTR(bytes_warning);
@@ -925,6 +932,7 @@ config_as_dict(const PyConfig *config)
925932
SET_ITEM_WSTR(base_prefix);
926933
SET_ITEM_WSTR(exec_prefix);
927934
SET_ITEM_WSTR(base_exec_prefix);
935+
SET_ITEM_WSTR(platlibdir);
928936
SET_ITEM_INT(site_import);
929937
SET_ITEM_INT(bytes_warning);
930938
SET_ITEM_INT(inspect);
@@ -1336,6 +1344,14 @@ config_read_env_vars(PyConfig *config)
13361344
}
13371345
}
13381346

1347+
if(config->platlibdir == NULL) {
1348+
status = CONFIG_GET_ENV_DUP(config, &config->platlibdir,
1349+
L"PYTHONPLATLIBDIR", "PYTHONPLATLIBDIR");
1350+
if (_PyStatus_EXCEPTION(status)) {
1351+
return status;
1352+
}
1353+
}
1354+
13391355
if (config->use_hash_seed < 0) {
13401356
status = config_init_hash_seed(config);
13411357
if (_PyStatus_EXCEPTION(status)) {
@@ -1731,6 +1747,14 @@ config_read(PyConfig *config)
17311747
}
17321748
}
17331749

1750+
if(config->platlibdir == NULL) {
1751+
status = CONFIG_SET_BYTES_STR(config, &config->platlibdir, PLATLIBDIR,
1752+
"PLATLIBDIR macro");
1753+
if (_PyStatus_EXCEPTION(status)) {
1754+
return status;
1755+
}
1756+
}
1757+
17341758
if (config->_install_importlib) {
17351759
status = _PyConfig_InitPathConfig(config);
17361760
if (_PyStatus_EXCEPTION(status)) {
@@ -2560,6 +2584,7 @@ PyConfig_Read(PyConfig *config)
25602584
assert(config->exec_prefix != NULL);
25612585
assert(config->base_exec_prefix != NULL);
25622586
}
2587+
assert(config->platlibdir != NULL);
25632588
assert(config->filesystem_encoding != NULL);
25642589
assert(config->filesystem_errors != NULL);
25652590
assert(config->stdio_encoding != NULL);
@@ -2710,6 +2735,7 @@ _Py_DumpPathConfig(PyThreadState *tstate)
27102735
DUMP_SYS(_base_executable);
27112736
DUMP_SYS(base_prefix);
27122737
DUMP_SYS(base_exec_prefix);
2738+
DUMP_SYS(platlibdir);
27132739
DUMP_SYS(executable);
27142740
DUMP_SYS(prefix);
27152741
DUMP_SYS(exec_prefix);

Python/sysmodule.c

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2922,13 +2922,7 @@ _PySys_InitMain(PyThreadState *tstate)
29222922
SET_SYS_FROM_WSTR("base_prefix", config->base_prefix);
29232923
SET_SYS_FROM_WSTR("exec_prefix", config->exec_prefix);
29242924
SET_SYS_FROM_WSTR("base_exec_prefix", config->base_exec_prefix);
2925-
{
2926-
PyObject *str = PyUnicode_FromString(PLATLIBDIR);
2927-
if (str == NULL) {
2928-
return -1;
2929-
}
2930-
SET_SYS_FROM_STRING("platlibdir", str);
2931-
}
2925+
SET_SYS_FROM_WSTR("platlibdir", config->platlibdir);
29322926

29332927
if (config->pycache_prefix != NULL) {
29342928
SET_SYS_FROM_WSTR("pycache_prefix", config->pycache_prefix);

0 commit comments

Comments
 (0)