Skip to content

Commit 221fd84

Browse files
authored
bpo-38234: Cleanup getpath.c (GH-16367)
* search_for_prefix() directly calls reduce() if found is greater than 0. * Add calculate_pybuilddir() subfunction. * search_for_prefix(): add path string buffer for readability. * Fix some error handling code paths: release resources on error. * calculate_read_pyenv(): rename tmpbuffer to filename. * test.pythoninfo now also logs windows.dll_path
1 parent 52ad33a commit 221fd84

File tree

3 files changed

+138
-88
lines changed

3 files changed

+138
-88
lines changed

Lib/test/pythoninfo.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,13 @@ def collect_windows(info_add):
673673
res = bool(RtlAreLongPathsEnabled())
674674
info_add('windows.RtlAreLongPathsEnabled', res)
675675

676+
try:
677+
import _winapi
678+
dll_path = _winapi.GetModuleFileName(sys.dllhandle)
679+
info_add('windows.dll_path', dll_path)
680+
except (ImportError, AttributeError):
681+
pass
682+
676683

677684
def collect_info(info):
678685
error = False

Modules/getpath.c

Lines changed: 116 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -370,12 +370,15 @@ search_for_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
370370
const wchar_t *argv0_path,
371371
wchar_t *prefix, size_t prefix_len, int *found)
372372
{
373+
wchar_t path[MAXPATHLEN+1];
374+
memset(path, 0, sizeof(path));
375+
size_t path_len = Py_ARRAY_LENGTH(path);
376+
373377
PyStatus status;
374-
size_t n;
375-
wchar_t *vpath;
376378

377379
/* If PYTHONHOME is set, we believe it unconditionally */
378380
if (pathconfig->home) {
381+
/* Path: <home> / <lib_python> */
379382
if (safe_wcscpy(prefix, pathconfig->home, prefix_len) < 0) {
380383
return PATHLEN_ERR();
381384
}
@@ -387,27 +390,25 @@ search_for_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
387390
if (_PyStatus_EXCEPTION(status)) {
388391
return status;
389392
}
390-
status = joinpath(prefix, LANDMARK, prefix_len);
391-
if (_PyStatus_EXCEPTION(status)) {
392-
return status;
393-
}
394393
*found = 1;
395394
return _PyStatus_OK();
396395
}
397396

398397
/* Check to see if argv[0] is in the build directory */
399-
if (safe_wcscpy(prefix, argv0_path, prefix_len) < 0) {
398+
if (safe_wcscpy(path, argv0_path, path_len) < 0) {
400399
return PATHLEN_ERR();
401400
}
402-
status = joinpath(prefix, L"Modules/Setup.local", prefix_len);
401+
status = joinpath(path, L"Modules/Setup.local", path_len);
403402
if (_PyStatus_EXCEPTION(status)) {
404403
return status;
405404
}
406405

407-
if (isfile(prefix)) {
408-
/* Check VPATH to see if argv0_path is in the build directory. */
409-
vpath = Py_DecodeLocale(VPATH, NULL);
406+
if (isfile(path)) {
407+
/* Check VPATH to see if argv0_path is in the build directory.
408+
VPATH can be empty. */
409+
wchar_t *vpath = Py_DecodeLocale(VPATH, NULL);
410410
if (vpath != NULL) {
411+
/* Path: <argv0_path> / <vpath> / Lib / LANDMARK */
411412
if (safe_wcscpy(prefix, argv0_path, prefix_len) < 0) {
412413
return PATHLEN_ERR();
413414
}
@@ -428,6 +429,7 @@ search_for_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
428429

429430
if (ismodule(prefix, prefix_len)) {
430431
*found = -1;
432+
reduce(prefix);
431433
return _PyStatus_OK();
432434
}
433435
}
@@ -440,7 +442,8 @@ search_for_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
440442
}
441443

442444
do {
443-
n = wcslen(prefix);
445+
/* Path: <argv0_path or substring> / <lib_python> / LANDMARK */
446+
size_t n = wcslen(prefix);
444447
status = joinpath(prefix, calculate->lib_python, prefix_len);
445448
if (_PyStatus_EXCEPTION(status)) {
446449
return status;
@@ -452,13 +455,15 @@ search_for_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
452455

453456
if (ismodule(prefix, prefix_len)) {
454457
*found = 1;
458+
reduce(prefix);
455459
return _PyStatus_OK();
456460
}
457461
prefix[n] = L'\0';
458462
reduce(prefix);
459463
} while (prefix[0]);
460464

461-
/* Look at configure's PREFIX */
465+
/* Look at configure's PREFIX.
466+
Path: <PREFIX macro> / <lib_python> / LANDMARK */
462467
if (safe_wcscpy(prefix, calculate->prefix, prefix_len) < 0) {
463468
return PATHLEN_ERR();
464469
}
@@ -473,6 +478,7 @@ search_for_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
473478

474479
if (ismodule(prefix, prefix_len)) {
475480
*found = 1;
481+
reduce(prefix);
476482
return _PyStatus_OK();
477483
}
478484

@@ -509,9 +515,6 @@ calculate_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
509515
return status;
510516
}
511517
}
512-
else {
513-
reduce(prefix);
514-
}
515518
return _PyStatus_OK();
516519
}
517520

@@ -546,6 +549,67 @@ calculate_set_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
546549
}
547550

548551

552+
static PyStatus
553+
calculate_pybuilddir(const wchar_t *argv0_path,
554+
wchar_t *exec_prefix, size_t exec_prefix_len,
555+
int *found)
556+
{
557+
PyStatus status;
558+
559+
wchar_t filename[MAXPATHLEN+1];
560+
memset(filename, 0, sizeof(filename));
561+
size_t filename_len = Py_ARRAY_LENGTH(filename);
562+
563+
/* Check to see if argv[0] is in the build directory. "pybuilddir.txt"
564+
is written by setup.py and contains the relative path to the location
565+
of shared library modules.
566+
567+
Filename: <argv0_path> / "pybuilddir.txt" */
568+
if (safe_wcscpy(filename, argv0_path, filename_len) < 0) {
569+
return PATHLEN_ERR();
570+
}
571+
status = joinpath(filename, L"pybuilddir.txt", filename_len);
572+
if (_PyStatus_EXCEPTION(status)) {
573+
return status;
574+
}
575+
576+
if (!isfile(filename)) {
577+
return _PyStatus_OK();
578+
}
579+
580+
FILE *fp = _Py_wfopen(filename, L"rb");
581+
if (fp == NULL) {
582+
errno = 0;
583+
return _PyStatus_OK();
584+
}
585+
586+
char buf[MAXPATHLEN + 1];
587+
size_t n = fread(buf, 1, Py_ARRAY_LENGTH(buf) - 1, fp);
588+
buf[n] = '\0';
589+
fclose(fp);
590+
591+
size_t dec_len;
592+
wchar_t *pybuilddir = _Py_DecodeUTF8_surrogateescape(buf, n, &dec_len);
593+
if (!pybuilddir) {
594+
return DECODE_LOCALE_ERR("pybuilddir.txt", dec_len);
595+
}
596+
597+
/* Path: <argv0_path> / <pybuilddir content> */
598+
if (safe_wcscpy(exec_prefix, argv0_path, exec_prefix_len) < 0) {
599+
PyMem_RawFree(pybuilddir);
600+
return PATHLEN_ERR();
601+
}
602+
status = joinpath(exec_prefix, pybuilddir, exec_prefix_len);
603+
PyMem_RawFree(pybuilddir);
604+
if (_PyStatus_EXCEPTION(status)) {
605+
return status;
606+
}
607+
608+
*found = -1;
609+
return _PyStatus_OK();
610+
}
611+
612+
549613
/* search_for_exec_prefix requires that argv0_path be no more than
550614
MAXPATHLEN bytes long.
551615
*/
@@ -556,10 +620,10 @@ search_for_exec_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
556620
int *found)
557621
{
558622
PyStatus status;
559-
size_t n;
560623

561624
/* If PYTHONHOME is set, we believe it unconditionally */
562625
if (pathconfig->home) {
626+
/* Path: <home> / <lib_python> / "lib-dynload" */
563627
wchar_t *delim = wcschr(pathconfig->home, DELIM);
564628
if (delim) {
565629
if (safe_wcscpy(exec_prefix, delim+1, exec_prefix_len) < 0) {
@@ -583,47 +647,15 @@ search_for_exec_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
583647
return _PyStatus_OK();
584648
}
585649

586-
/* Check to see if argv[0] is in the build directory. "pybuilddir.txt"
587-
is written by setup.py and contains the relative path to the location
588-
of shared library modules. */
589-
if (safe_wcscpy(exec_prefix, argv0_path, exec_prefix_len) < 0) {
590-
return PATHLEN_ERR();
591-
}
592-
status = joinpath(exec_prefix, L"pybuilddir.txt", exec_prefix_len);
650+
/* Check for pybuilddir.txt */
651+
assert(*found == 0);
652+
status = calculate_pybuilddir(argv0_path, exec_prefix, exec_prefix_len,
653+
found);
593654
if (_PyStatus_EXCEPTION(status)) {
594655
return status;
595656
}
596-
597-
if (isfile(exec_prefix)) {
598-
FILE *f = _Py_wfopen(exec_prefix, L"rb");
599-
if (f == NULL) {
600-
errno = 0;
601-
}
602-
else {
603-
char buf[MAXPATHLEN + 1];
604-
n = fread(buf, 1, Py_ARRAY_LENGTH(buf) - 1, f);
605-
buf[n] = '\0';
606-
fclose(f);
607-
608-
wchar_t *pybuilddir;
609-
size_t dec_len;
610-
pybuilddir = _Py_DecodeUTF8_surrogateescape(buf, n, &dec_len);
611-
if (!pybuilddir) {
612-
return DECODE_LOCALE_ERR("pybuilddir.txt", dec_len);
613-
}
614-
615-
if (safe_wcscpy(exec_prefix, argv0_path, exec_prefix_len) < 0) {
616-
return PATHLEN_ERR();
617-
}
618-
status = joinpath(exec_prefix, pybuilddir, exec_prefix_len);
619-
PyMem_RawFree(pybuilddir );
620-
if (_PyStatus_EXCEPTION(status)) {
621-
return status;
622-
}
623-
624-
*found = -1;
625-
return _PyStatus_OK();
626-
}
657+
if (*found) {
658+
return _PyStatus_OK();
627659
}
628660

629661
/* Search from argv0_path, until root is found */
@@ -633,7 +665,8 @@ search_for_exec_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
633665
}
634666

635667
do {
636-
n = wcslen(exec_prefix);
668+
/* Path: <argv0_path or substring> / <lib_python> / "lib-dynload" */
669+
size_t n = wcslen(exec_prefix);
637670
status = joinpath(exec_prefix, calculate->lib_python, exec_prefix_len);
638671
if (_PyStatus_EXCEPTION(status)) {
639672
return status;
@@ -650,7 +683,9 @@ search_for_exec_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
650683
reduce(exec_prefix);
651684
} while (exec_prefix[0]);
652685

653-
/* Look at configure's EXEC_PREFIX */
686+
/* Look at configure's EXEC_PREFIX.
687+
688+
Path: <EXEC_PREFIX macro> / <lib_python> / "lib-dynload" */
654689
if (safe_wcscpy(exec_prefix, calculate->exec_prefix, exec_prefix_len) < 0) {
655690
return PATHLEN_ERR();
656691
}
@@ -962,43 +997,49 @@ calculate_read_pyenv(PyCalculatePath *calculate,
962997
wchar_t *argv0_path, size_t argv0_path_len)
963998
{
964999
PyStatus status;
965-
wchar_t tmpbuffer[MAXPATHLEN+1];
966-
const size_t buflen = Py_ARRAY_LENGTH(tmpbuffer);
967-
wchar_t *env_cfg = L"pyvenv.cfg";
1000+
const wchar_t *env_cfg = L"pyvenv.cfg";
9681001
FILE *env_file;
9691002

970-
if (safe_wcscpy(tmpbuffer, argv0_path, buflen) < 0) {
1003+
wchar_t filename[MAXPATHLEN+1];
1004+
const size_t filename_len = Py_ARRAY_LENGTH(filename);
1005+
memset(filename, 0, sizeof(filename));
1006+
1007+
/* Filename: <argv0_path_len> / "pyvenv.cfg" */
1008+
if (safe_wcscpy(filename, argv0_path, filename_len) < 0) {
9711009
return PATHLEN_ERR();
9721010
}
9731011

974-
status = joinpath(tmpbuffer, env_cfg, buflen);
1012+
status = joinpath(filename, env_cfg, filename_len);
9751013
if (_PyStatus_EXCEPTION(status)) {
9761014
return status;
9771015
}
978-
env_file = _Py_wfopen(tmpbuffer, L"r");
1016+
env_file = _Py_wfopen(filename, L"r");
9791017
if (env_file == NULL) {
9801018
errno = 0;
9811019

982-
reduce(tmpbuffer);
983-
reduce(tmpbuffer);
984-
status = joinpath(tmpbuffer, env_cfg, buflen);
1020+
/* Filename: <basename(basename(argv0_path_len))> / "pyvenv.cfg" */
1021+
reduce(filename);
1022+
reduce(filename);
1023+
status = joinpath(filename, env_cfg, filename_len);
9851024
if (_PyStatus_EXCEPTION(status)) {
9861025
return status;
9871026
}
9881027

989-
env_file = _Py_wfopen(tmpbuffer, L"r");
1028+
env_file = _Py_wfopen(filename, L"r");
9901029
if (env_file == NULL) {
9911030
errno = 0;
1031+
return _PyStatus_OK();
9921032
}
9931033
}
9941034

995-
if (env_file == NULL) {
996-
return _PyStatus_OK();
997-
}
998-
9991035
/* Look for a 'home' variable and set argv0_path to it, if found */
1000-
if (_Py_FindEnvConfigValue(env_file, L"home", tmpbuffer, buflen)) {
1001-
if (safe_wcscpy(argv0_path, tmpbuffer, argv0_path_len) < 0) {
1036+
wchar_t home[MAXPATHLEN+1];
1037+
memset(home, 0, sizeof(home));
1038+
1039+
if (_Py_FindEnvConfigValue(env_file, L"home",
1040+
home, Py_ARRAY_LENGTH(home))) {
1041+
if (safe_wcscpy(argv0_path, home, argv0_path_len) < 0) {
1042+
fclose(env_file);
10021043
return PATHLEN_ERR();
10031044
}
10041045
}
@@ -1200,6 +1241,8 @@ calculate_path(PyCalculatePath *calculate, _PyPathConfig *pathconfig)
12001241
return status;
12011242
}
12021243

1244+
/* If a pyvenv.cfg configure file is found,
1245+
argv0_path is overriden with its 'home' variable. */
12031246
status = calculate_read_pyenv(calculate,
12041247
argv0_path, Py_ARRAY_LENGTH(argv0_path));
12051248
if (_PyStatus_EXCEPTION(status)) {

PC/getpathp.c

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -757,34 +757,34 @@ static void
757757
calculate_pyvenv_file(PyCalculatePath *calculate,
758758
wchar_t *argv0_path, size_t argv0_path_len)
759759
{
760-
wchar_t envbuffer[MAXPATHLEN+1];
760+
wchar_t filename[MAXPATHLEN+1];
761761
const wchar_t *env_cfg = L"pyvenv.cfg";
762762

763-
wcscpy_s(envbuffer, MAXPATHLEN+1, argv0_path);
764-
join(envbuffer, env_cfg);
763+
/* Filename: <argv0_path_len> / "pyvenv.cfg" */
764+
wcscpy_s(filename, MAXPATHLEN+1, argv0_path);
765+
join(filename, env_cfg);
765766

766-
FILE *env_file = _Py_wfopen(envbuffer, L"r");
767+
FILE *env_file = _Py_wfopen(filename, L"r");
767768
if (env_file == NULL) {
768769
errno = 0;
769770

770-
reduce(envbuffer);
771-
reduce(envbuffer);
772-
join(envbuffer, env_cfg);
771+
/* Filename: <basename(basename(argv0_path_len))> / "pyvenv.cfg" */
772+
reduce(filename);
773+
reduce(filename);
774+
join(filename, env_cfg);
773775

774-
env_file = _Py_wfopen(envbuffer, L"r");
776+
env_file = _Py_wfopen(filename, L"r");
775777
if (env_file == NULL) {
776778
errno = 0;
779+
return;
777780
}
778781
}
779782

780-
if (env_file == NULL) {
781-
return;
782-
}
783-
784783
/* Look for a 'home' variable and set argv0_path to it, if found */
785-
wchar_t tmpbuffer[MAXPATHLEN+1];
786-
if (_Py_FindEnvConfigValue(env_file, L"home", tmpbuffer, MAXPATHLEN)) {
787-
wcscpy_s(argv0_path, argv0_path_len, tmpbuffer);
784+
wchar_t home[MAXPATHLEN+1];
785+
if (_Py_FindEnvConfigValue(env_file, L"home",
786+
home, Py_ARRAY_LENGTH(home))) {
787+
wcscpy_s(argv0_path, argv0_path_len, home);
788788
}
789789
fclose(env_file);
790790
}

0 commit comments

Comments
 (0)