Skip to content

Commit d434110

Browse files
authored
bpo-32030: Add _PyCoreConfig.module_search_path_env (#4504)
Changes: * Py_Main() initializes _PyCoreConfig.module_search_path_env from the PYTHONPATH environment variable. * PyInterpreterState_New() now initializes core_config and config fields * Compute sys.path a little bit ealier in _Py_InitializeMainInterpreter() and new_interpreter() * Add _Py_GetPathWithConfig() private function.
1 parent 8265627 commit d434110

File tree

7 files changed

+189
-86
lines changed

7 files changed

+189
-86
lines changed

‎Include/pylifecycle.h‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ PyAPI_FUNC(wchar_t *) Py_GetProgramFullPath(void);
9393
PyAPI_FUNC(wchar_t *) Py_GetPrefix(void);
9494
PyAPI_FUNC(wchar_t *) Py_GetExecPrefix(void);
9595
PyAPI_FUNC(wchar_t *) Py_GetPath(void);
96+
#ifdef Py_BUILD_CORE
97+
PyAPI_FUNC(wchar_t *) _Py_GetPathWithConfig(_PyCoreConfig *config);
98+
#endif
9699
PyAPI_FUNC(void) Py_SetPath(const wchar_t *);
97100
#ifdef MS_WINDOWS
98101
int _Py_CheckPython3();

‎Include/pystate.h‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ typedef struct {
3030
unsigned long hash_seed;
3131
int _disable_importlib; /* Needed by freeze_importlib */
3232
const char *allocator; /* Memory allocator: _PyMem_SetupAllocators() */
33+
wchar_t *module_search_path_env; /* PYTHONPATH environment variable */
3334
int dev_mode; /* -X dev */
3435
int faulthandler; /* -X faulthandler */
3536
int tracemalloc; /* -X tracemalloc=N */
@@ -39,11 +40,13 @@ typedef struct {
3940
} _PyCoreConfig;
4041

4142
#define _PyCoreConfig_INIT \
42-
{.ignore_environment = 0, \
43+
(_PyCoreConfig){\
44+
.ignore_environment = 0, \
4345
.use_hash_seed = -1, \
4446
.hash_seed = 0, \
4547
._disable_importlib = 0, \
4648
.allocator = NULL, \
49+
.module_search_path_env = NULL, \
4750
.dev_mode = 0, \
4851
.faulthandler = 0, \
4952
.tracemalloc = 0, \
@@ -61,7 +64,9 @@ typedef struct {
6164
int install_signal_handlers;
6265
} _PyMainInterpreterConfig;
6366

64-
#define _PyMainInterpreterConfig_INIT {-1}
67+
#define _PyMainInterpreterConfig_INIT \
68+
(_PyMainInterpreterConfig){\
69+
.install_signal_handlers = -1}
6570

6671
typedef struct _is {
6772

‎Modules/getpath.c‎

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -456,14 +456,12 @@ search_for_exec_prefix(wchar_t *argv0_path, wchar_t *home,
456456
}
457457

458458
static void
459-
calculate_path(void)
459+
calculate_path(_PyCoreConfig *core_config)
460460
{
461461
extern wchar_t *Py_GetProgramName(void);
462462

463463
static const wchar_t delimiter[2] = {DELIM, '\0'};
464464
static const wchar_t separator[2] = {SEP, '\0'};
465-
char *_rtpypath = Py_GETENV("PYTHONPATH"); /* XXX use wide version on Windows */
466-
wchar_t *rtpypath = NULL;
467465
wchar_t *home = Py_GetPythonHome();
468466
char *_path = getenv("PATH");
469467
wchar_t *path_buffer = NULL;
@@ -707,11 +705,22 @@ calculate_path(void)
707705
*/
708706
bufsz = 0;
709707

710-
if (_rtpypath && _rtpypath[0] != '\0') {
711-
size_t rtpypath_len;
712-
rtpypath = Py_DecodeLocale(_rtpypath, &rtpypath_len);
713-
if (rtpypath != NULL)
714-
bufsz += rtpypath_len + 1;
708+
wchar_t *env_path = NULL;
709+
if (core_config) {
710+
if (core_config->module_search_path_env) {
711+
bufsz += wcslen(core_config->module_search_path_env) + 1;
712+
}
713+
}
714+
else {
715+
char *env_pathb = Py_GETENV("PYTHONPATH");
716+
if (env_pathb && env_pathb[0] != '\0') {
717+
size_t env_path_len;
718+
env_path = Py_DecodeLocale(env_pathb, &env_path_len);
719+
/* FIXME: handle decoding and memory error */
720+
if (env_path != NULL) {
721+
bufsz += env_path_len + 1;
722+
}
723+
}
715724
}
716725

717726
defpath = _pythonpath;
@@ -742,12 +751,20 @@ calculate_path(void)
742751
}
743752

744753
/* Run-time value of $PYTHONPATH goes first */
745-
if (rtpypath) {
746-
wcscpy(buf, rtpypath);
747-
wcscat(buf, delimiter);
754+
buf[0] = '\0';
755+
if (core_config) {
756+
if (core_config->module_search_path_env) {
757+
wcscpy(buf, core_config->module_search_path_env);
758+
wcscat(buf, delimiter);
759+
}
748760
}
749-
else
750-
buf[0] = '\0';
761+
else {
762+
if (env_path) {
763+
wcscpy(buf, env_path);
764+
wcscat(buf, delimiter);
765+
}
766+
}
767+
PyMem_RawFree(env_path);
751768

752769
/* Next is the default zip path */
753770
wcscat(buf, zip_path);
@@ -818,7 +835,6 @@ calculate_path(void)
818835
PyMem_RawFree(_prefix);
819836
PyMem_RawFree(_exec_prefix);
820837
PyMem_RawFree(lib_python);
821-
PyMem_RawFree(rtpypath);
822838
}
823839

824840

@@ -841,35 +857,44 @@ Py_SetPath(const wchar_t *path)
841857
}
842858
}
843859

860+
wchar_t *
861+
_Py_GetPathWithConfig(_PyCoreConfig *core_config)
862+
{
863+
if (!module_search_path) {
864+
calculate_path(core_config);
865+
}
866+
return module_search_path;
867+
}
868+
844869
wchar_t *
845870
Py_GetPath(void)
846871
{
847872
if (!module_search_path)
848-
calculate_path();
873+
calculate_path(NULL);
849874
return module_search_path;
850875
}
851876

852877
wchar_t *
853878
Py_GetPrefix(void)
854879
{
855880
if (!module_search_path)
856-
calculate_path();
881+
calculate_path(NULL);
857882
return prefix;
858883
}
859884

860885
wchar_t *
861886
Py_GetExecPrefix(void)
862887
{
863888
if (!module_search_path)
864-
calculate_path();
889+
calculate_path(NULL);
865890
return exec_prefix;
866891
}
867892

868893
wchar_t *
869894
Py_GetProgramFullPath(void)
870895
{
871896
if (!module_search_path)
872-
calculate_path();
897+
calculate_path(NULL);
873898
return progpath;
874899
}
875900

‎Modules/main.c‎

Lines changed: 63 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,7 @@ typedef struct {
399399
_PyInitError err;
400400
/* PYTHONWARNINGS env var */
401401
_Py_OptList env_warning_options;
402+
/* PYTHONPATH env var */
402403
int argc;
403404
wchar_t **argv;
404405
} _PyMain;
@@ -441,6 +442,8 @@ pymain_free_impl(_PyMain *pymain)
441442
Py_CLEAR(pymain->main_importer_path);
442443
PyMem_RawFree(pymain->program_name);
443444

445+
PyMem_RawFree(pymain->core_config.module_search_path_env);
446+
444447
#ifdef __INSURE__
445448
/* Insure++ is a memory analysis tool that aids in discovering
446449
* memory leaks and other memory problems. On Python exit, the
@@ -502,23 +505,30 @@ pymain_run_main_from_importer(_PyMain *pymain)
502505

503506

504507
static wchar_t*
505-
pymain_strdup(_PyMain *pymain, wchar_t *str)
508+
pymain_wstrdup(_PyMain *pymain, wchar_t *str)
506509
{
507510
size_t len = wcslen(str) + 1; /* +1 for NUL character */
508-
wchar_t *str2 = PyMem_RawMalloc(sizeof(wchar_t) * len);
511+
if (len > (size_t)PY_SSIZE_T_MAX / sizeof(wchar_t)) {
512+
pymain->err = INIT_NO_MEMORY();
513+
return NULL;
514+
}
515+
516+
size_t size = len * sizeof(wchar_t);
517+
wchar_t *str2 = PyMem_RawMalloc(size);
509518
if (str2 == NULL) {
510519
pymain->err = INIT_NO_MEMORY();
511520
return NULL;
512521
}
513-
memcpy(str2, str, len * sizeof(wchar_t));
522+
523+
memcpy(str2, str, size);
514524
return str2;
515525
}
516526

517527

518528
static int
519529
pymain_optlist_append(_PyMain *pymain, _Py_OptList *list, wchar_t *str)
520530
{
521-
wchar_t *str2 = pymain_strdup(pymain, str);
531+
wchar_t *str2 = pymain_wstrdup(pymain, str);
522532
if (str2 == NULL) {
523533
return -1;
524534
}
@@ -762,14 +772,12 @@ pymain_warnings_envvar(_PyMain *pymain)
762772
wchar_t *wp;
763773

764774
if ((wp = _wgetenv(L"PYTHONWARNINGS")) && *wp != L'\0') {
765-
wchar_t *buf, *warning, *context = NULL;
775+
wchar_t *warning, *context = NULL;
766776

767-
buf = (wchar_t *)PyMem_RawMalloc((wcslen(wp) + 1) * sizeof(wchar_t));
777+
wchar_t *buf = pymain_wstrdup(pymain, wp);
768778
if (buf == NULL) {
769-
pymain->err = INIT_NO_MEMORY();
770779
return -1;
771780
}
772-
wcscpy(buf, wp);
773781
for (warning = wcstok_s(buf, L",", &context);
774782
warning != NULL;
775783
warning = wcstok_s(NULL, L",", &context)) {
@@ -805,12 +813,11 @@ pymain_warnings_envvar(_PyMain *pymain)
805813
if (len == (size_t)-2) {
806814
pymain->err = _Py_INIT_ERR("failed to decode "
807815
"PYTHONWARNINGS");
808-
return -1;
809816
}
810817
else {
811818
pymain->err = INIT_NO_MEMORY();
812-
return -1;
813819
}
820+
return -1;
814821
}
815822
if (pymain_optlist_append(pymain, &pymain->env_warning_options,
816823
warning) < 0) {
@@ -929,7 +936,7 @@ pymain_get_program_name(_PyMain *pymain)
929936

930937
if (pymain->program_name == NULL) {
931938
/* Use argv[0] by default */
932-
pymain->program_name = pymain_strdup(pymain, pymain->argv[0]);
939+
pymain->program_name = pymain_wstrdup(pymain, pymain->argv[0]);
933940
if (pymain->program_name == NULL) {
934941
return -1;
935942
}
@@ -1362,6 +1369,48 @@ pymain_set_flags_from_env(_PyMain *pymain)
13621369
}
13631370

13641371

1372+
static int
1373+
pymain_init_pythonpath(_PyMain *pymain)
1374+
{
1375+
if (Py_IgnoreEnvironmentFlag) {
1376+
return 0;
1377+
}
1378+
1379+
#ifdef MS_WINDOWS
1380+
wchar_t *path = _wgetenv(L"PYTHONPATH");
1381+
if (!path || path[0] == '\0') {
1382+
return 0;
1383+
}
1384+
1385+
wchar_t *path2 = pymain_wstrdup(pymain, path);
1386+
if (path2 == NULL) {
1387+
return -1;
1388+
}
1389+
1390+
pymain->core_config.module_search_path_env = path2;
1391+
#else
1392+
char *path = pymain_get_env_var("PYTHONPATH");
1393+
if (!path) {
1394+
return 0;
1395+
}
1396+
1397+
size_t len;
1398+
wchar_t *wpath = Py_DecodeLocale(path, &len);
1399+
if (!wpath) {
1400+
if (len == (size_t)-2) {
1401+
pymain->err = _Py_INIT_ERR("failed to decode PYTHONHOME");
1402+
}
1403+
else {
1404+
pymain->err = INIT_NO_MEMORY();
1405+
}
1406+
return -1;
1407+
}
1408+
pymain->core_config.module_search_path_env = wpath;
1409+
#endif
1410+
return 0;
1411+
}
1412+
1413+
13651414
static int
13661415
pymain_parse_envvars(_PyMain *pymain)
13671416
{
@@ -1383,6 +1432,9 @@ pymain_parse_envvars(_PyMain *pymain)
13831432
return -1;
13841433
}
13851434
core_config->allocator = Py_GETENV("PYTHONMALLOC");
1435+
if (pymain_init_pythonpath(pymain) < 0) {
1436+
return -1;
1437+
}
13861438

13871439
/* -X options */
13881440
if (pymain_get_xoption(pymain, L"showrefcount")) {

‎PC/getpathp.c‎

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -624,7 +624,7 @@ read_pth_file(const wchar_t *path, wchar_t *prefix, int *isolated, int *nosite)
624624

625625

626626
static void
627-
calculate_path(void)
627+
calculate_path(_PyCoreConfig *core_config)
628628
{
629629
wchar_t argv0_path[MAXPATHLEN+1];
630630
wchar_t *buf;
@@ -637,8 +637,13 @@ calculate_path(void)
637637
wchar_t *userpath = NULL;
638638
wchar_t zip_path[MAXPATHLEN+1];
639639

640-
if (!Py_IgnoreEnvironmentFlag) {
640+
if (core_config) {
641+
envpath = core_config->module_search_path_env;
642+
}
643+
else if (!Py_IgnoreEnvironmentFlag) {
641644
envpath = _wgetenv(L"PYTHONPATH");
645+
if (envpath && *envpath == '\0')
646+
envpath = NULL;
642647
}
643648

644649
get_progpath();
@@ -709,9 +714,6 @@ calculate_path(void)
709714
else
710715
wcscpy_s(prefix, MAXPATHLEN+1, pythonhome);
711716

712-
if (envpath && *envpath == '\0')
713-
envpath = NULL;
714-
715717

716718
skiphome = pythonhome==NULL ? 0 : 1;
717719
#ifdef Py_ENABLE_SHARED
@@ -896,19 +898,28 @@ Py_SetPath(const wchar_t *path)
896898
}
897899
}
898900

901+
wchar_t *
902+
_Py_GetPathWithConfig(_PyCoreConfig *core_config)
903+
{
904+
if (!module_search_path) {
905+
calculate_path(core_config);
906+
}
907+
return module_search_path;
908+
}
909+
899910
wchar_t *
900911
Py_GetPath(void)
901912
{
902913
if (!module_search_path)
903-
calculate_path();
914+
calculate_path(NULL);
904915
return module_search_path;
905916
}
906917

907918
wchar_t *
908919
Py_GetPrefix(void)
909920
{
910921
if (!module_search_path)
911-
calculate_path();
922+
calculate_path(NULL);
912923
return prefix;
913924
}
914925

@@ -922,7 +933,7 @@ wchar_t *
922933
Py_GetProgramFullPath(void)
923934
{
924935
if (!module_search_path)
925-
calculate_path();
936+
calculate_path(NULL);
926937
return progpath;
927938
}
928939

0 commit comments

Comments
 (0)