Skip to content

Commit 11a247d

Browse files
authored
bpo-32030: Add _PyPathConfig_ComputeArgv0() (#4845)
Changes: * Split _PySys_SetArgvWithError() into subfunctions for Py_Main(): * Create the Python list object * Set sys.argv to the list * Compute argv0 * Prepend argv0 to sys.path * Add _PyPathConfig_ComputeArgv0() * Remove _PySys_SetArgvWithError() * Py_Main() now splits the code to compute sys.argv/path0 and the code to update the sys module: add pymain_compute_argv() subfunction.
1 parent a70232f commit 11a247d

File tree

5 files changed

+207
-141
lines changed

5 files changed

+207
-141
lines changed

Include/pylifecycle.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ PyAPI_FUNC(wchar_t *) Py_GetPath(void);
105105
#ifdef Py_BUILD_CORE
106106
PyAPI_FUNC(_PyInitError) _PyPathConfig_Init(
107107
const _PyMainInterpreterConfig *main_config);
108+
PyAPI_FUNC(PyObject*) _PyPathConfig_ComputeArgv0(int argc, wchar_t **argv);
108109
#endif
109110
PyAPI_FUNC(void) Py_SetPath(const wchar_t *);
110111
#ifdef MS_WINDOWS

Include/sysmodule.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,6 @@ PyAPI_FUNC(int) _PySys_SetObjectId(_Py_Identifier *key, PyObject *);
1616

1717
PyAPI_FUNC(void) PySys_SetArgv(int, wchar_t **);
1818
PyAPI_FUNC(void) PySys_SetArgvEx(int, wchar_t **, int);
19-
#ifdef Py_BUILD_CORE
20-
PyAPI_FUNC(_PyInitError) _PySys_SetArgvWithError(
21-
int argc,
22-
wchar_t **argv,
23-
int updatepath);
24-
#endif
2519
PyAPI_FUNC(void) PySys_SetPath(const wchar_t *);
2620

2721
PyAPI_FUNC(void) PySys_WriteStdout(const char *format, ...)

Modules/main.c

Lines changed: 91 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,13 @@ typedef struct {
410410
#endif
411411
} _Py_CommandLineDetails;
412412

413+
/* FIXME: temporary structure until sys module configuration can be moved
414+
into _PyMainInterpreterConfig */
415+
typedef struct {
416+
PyObject *argv; /* sys.argv list */
417+
PyObject *path0; /* path0: if set, it is prepended to sys.path */
418+
} _PySysConfig;
419+
413420
/* Structure used by Py_Main() to pass data to subfunctions */
414421
typedef struct {
415422
/* Exit status ("exit code") */
@@ -419,6 +426,7 @@ typedef struct {
419426
int stdin_is_interactive;
420427
_PyCoreConfig core_config;
421428
_PyMainInterpreterConfig config;
429+
_PySysConfig sys_config;
422430
_Py_CommandLineDetails cmdline;
423431
PyObject *main_importer_path;
424432
/* non-zero if filename, command (-c) or module (-m) is set
@@ -492,6 +500,8 @@ pymain_free_pymain(_PyMain *pymain)
492500
pymain_optlist_clear(&pymain->env_warning_options);
493501
Py_CLEAR(pymain->main_importer_path);
494502

503+
Py_CLEAR(pymain->sys_config.argv);
504+
Py_CLEAR(pymain->sys_config.path0);
495505
}
496506

497507
static void
@@ -510,26 +520,20 @@ pymain_free(_PyMain *pymain)
510520
static int
511521
pymain_run_main_from_importer(_PyMain *pymain)
512522
{
513-
PyObject *sys_path0 = pymain->main_importer_path;
514-
PyObject *sys_path;
515-
int sts;
516-
517523
/* Assume sys_path0 has already been checked by pymain_get_importer(),
518524
* so put it in sys.path[0] and import __main__ */
519-
sys_path = PySys_GetObject("path");
525+
PyObject *sys_path = PySys_GetObject("path");
520526
if (sys_path == NULL) {
521527
PyErr_SetString(PyExc_RuntimeError, "unable to get sys.path");
522528
goto error;
523529
}
524530

525-
sts = PyList_Insert(sys_path, 0, sys_path0);
526-
if (sts) {
527-
sys_path0 = NULL;
531+
if (PyList_Insert(sys_path, 0, pymain->main_importer_path)) {
528532
goto error;
529533
}
530534

531-
sts = pymain_run_module(L"__main__", 0);
532-
return sts != 0;
535+
int sts = pymain_run_module(L"__main__", 0);
536+
return (sts != 0);
533537

534538
error:
535539
Py_CLEAR(pymain->main_importer_path);
@@ -1082,8 +1086,36 @@ pymain_header(_PyMain *pymain)
10821086
}
10831087

10841088

1089+
static PyObject *
1090+
pymain_create_argv_list(int argc, wchar_t **argv)
1091+
{
1092+
if (argc <= 0 || argv == NULL) {
1093+
/* Ensure at least one (empty) argument is seen */
1094+
static wchar_t *empty_argv[1] = {L""};
1095+
argv = empty_argv;
1096+
argc = 1;
1097+
}
1098+
1099+
PyObject *av = PyList_New(argc);
1100+
if (av == NULL) {
1101+
return NULL;
1102+
}
1103+
1104+
for (int i = 0; i < argc; i++) {
1105+
PyObject *v = PyUnicode_FromWideChar(argv[i], -1);
1106+
if (v == NULL) {
1107+
Py_DECREF(av);
1108+
return NULL;
1109+
}
1110+
PyList_SET_ITEM(av, i, v);
1111+
}
1112+
return av;
1113+
}
1114+
1115+
1116+
/* Create sys.argv list and maybe also path0 */
10851117
static int
1086-
pymain_set_sys_argv(_PyMain *pymain)
1118+
pymain_compute_argv(_PyMain *pymain)
10871119
{
10881120
_Py_CommandLineDetails *cmdline = &pymain->cmdline;
10891121

@@ -1112,6 +1144,14 @@ pymain_set_sys_argv(_PyMain *pymain)
11121144
argv2[0] = L"-m";
11131145
}
11141146

1147+
/* Create sys.argv list */
1148+
pymain->sys_config.argv = pymain_create_argv_list(argc2, argv2);
1149+
if (pymain->sys_config.argv == NULL) {
1150+
pymain->err = _Py_INIT_ERR("failed to create sys.argv");
1151+
goto error;
1152+
}
1153+
1154+
/* Need to update sys.path[0]? */
11151155
int update_path;
11161156
if (pymain->main_importer_path != NULL) {
11171157
/* Let pymain_run_main_from_importer() adjust sys.path[0] later */
@@ -1121,16 +1161,48 @@ pymain_set_sys_argv(_PyMain *pymain)
11211161
update_path = (Py_IsolatedFlag == 0);
11221162
}
11231163

1124-
/* Set sys.argv. If '-c' and '-m' options are not used in the command line
1164+
/* If '-c' and '-m' options are not used in the command line
11251165
and update_path is non-zero, prepend argv[0] to sys.path. If argv[0] is
11261166
a symlink, use the real path. */
1127-
_PyInitError err = _PySys_SetArgvWithError(argc2, argv2, update_path);
1128-
if (_Py_INIT_FAILED(err)) {
1129-
pymain->err = err;
1167+
if (update_path) {
1168+
pymain->sys_config.path0 = _PyPathConfig_ComputeArgv0(argc2, argv2);
1169+
if (pymain->sys_config.path0 == NULL) {
1170+
pymain->err = _Py_INIT_NO_MEMORY();
1171+
goto error;
1172+
}
1173+
}
1174+
PyMem_RawFree(argv2);
1175+
return 0;
1176+
1177+
error:
1178+
return -1;
1179+
}
1180+
1181+
static int
1182+
pymain_set_sys_argv(_PyMain *pymain)
1183+
{
1184+
/* Set sys.argv */
1185+
if (PySys_SetObject("argv", pymain->sys_config.argv) != 0) {
1186+
pymain->err = _Py_INIT_ERR("can't assign sys.argv");
11301187
return -1;
11311188
}
1189+
Py_CLEAR(pymain->sys_config.argv);
1190+
1191+
if (pymain->sys_config.path0 != NULL) {
1192+
/* Prepend path0 to sys.path */
1193+
PyObject *sys_path = PySys_GetObject("path");
1194+
if (sys_path == NULL) {
1195+
pymain->err = _Py_INIT_ERR("can't get sys.path");
1196+
return -1;
1197+
}
1198+
1199+
if (PyList_Insert(sys_path, 0, pymain->sys_config.path0) < 0) {
1200+
pymain->err = _Py_INIT_ERR("sys.path.insert(0, path0) failed");
1201+
return -1;
1202+
}
1203+
Py_CLEAR(pymain->sys_config.path0);
1204+
}
11321205

1133-
PyMem_RawFree(argv2);
11341206
return 0;
11351207
}
11361208

@@ -1822,6 +1894,9 @@ pymain_init_python(_PyMain *pymain)
18221894
Currently, PySys_SetArgvEx() can still modify sys.path and so must be
18231895
called after _Py_InitializeMainInterpreter() which calls
18241896
_PyPathConfig_Init(). */
1897+
if (pymain_compute_argv(pymain) < 0) {
1898+
return -1;
1899+
}
18251900
if (pymain_set_sys_argv(pymain) < 0) {
18261901
return -1;
18271902
}

Python/pathconfig.c

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,104 @@ Py_GetProgramName(void)
261261
}
262262

263263

264+
#define _HAVE_SCRIPT_ARGUMENT(argc, argv) \
265+
(argc > 0 && argv0 != NULL && \
266+
wcscmp(argv0, L"-c") != 0 && wcscmp(argv0, L"-m") != 0)
267+
268+
/* Compute argv[0] which will be prepended to sys.argv */
269+
PyObject*
270+
_PyPathConfig_ComputeArgv0(int argc, wchar_t **argv)
271+
{
272+
wchar_t *argv0;
273+
wchar_t *p = NULL;
274+
Py_ssize_t n = 0;
275+
#ifdef HAVE_READLINK
276+
wchar_t link[MAXPATHLEN+1];
277+
wchar_t argv0copy[2*MAXPATHLEN+1];
278+
int nr = 0;
279+
#endif
280+
#if defined(HAVE_REALPATH)
281+
wchar_t fullpath[MAXPATHLEN];
282+
#elif defined(MS_WINDOWS)
283+
wchar_t fullpath[MAX_PATH];
284+
#endif
285+
286+
287+
argv0 = argv[0];
288+
289+
#ifdef HAVE_READLINK
290+
if (_HAVE_SCRIPT_ARGUMENT(argc, argv))
291+
nr = _Py_wreadlink(argv0, link, MAXPATHLEN);
292+
if (nr > 0) {
293+
/* It's a symlink */
294+
link[nr] = '\0';
295+
if (link[0] == SEP)
296+
argv0 = link; /* Link to absolute path */
297+
else if (wcschr(link, SEP) == NULL)
298+
; /* Link without path */
299+
else {
300+
/* Must join(dirname(argv0), link) */
301+
wchar_t *q = wcsrchr(argv0, SEP);
302+
if (q == NULL)
303+
argv0 = link; /* argv0 without path */
304+
else {
305+
/* Must make a copy, argv0copy has room for 2 * MAXPATHLEN */
306+
wcsncpy(argv0copy, argv0, MAXPATHLEN);
307+
q = wcsrchr(argv0copy, SEP);
308+
wcsncpy(q+1, link, MAXPATHLEN);
309+
q[MAXPATHLEN + 1] = L'\0';
310+
argv0 = argv0copy;
311+
}
312+
}
313+
}
314+
#endif /* HAVE_READLINK */
315+
316+
#if SEP == '\\'
317+
/* Special case for Microsoft filename syntax */
318+
if (_HAVE_SCRIPT_ARGUMENT(argc, argv)) {
319+
wchar_t *q;
320+
#if defined(MS_WINDOWS)
321+
/* Replace the first element in argv with the full path. */
322+
wchar_t *ptemp;
323+
if (GetFullPathNameW(argv0,
324+
Py_ARRAY_LENGTH(fullpath),
325+
fullpath,
326+
&ptemp)) {
327+
argv0 = fullpath;
328+
}
329+
#endif
330+
p = wcsrchr(argv0, SEP);
331+
/* Test for alternate separator */
332+
q = wcsrchr(p ? p : argv0, '/');
333+
if (q != NULL)
334+
p = q;
335+
if (p != NULL) {
336+
n = p + 1 - argv0;
337+
if (n > 1 && p[-1] != ':')
338+
n--; /* Drop trailing separator */
339+
}
340+
}
341+
#else /* All other filename syntaxes */
342+
if (_HAVE_SCRIPT_ARGUMENT(argc, argv)) {
343+
#if defined(HAVE_REALPATH)
344+
if (_Py_wrealpath(argv0, fullpath, Py_ARRAY_LENGTH(fullpath))) {
345+
argv0 = fullpath;
346+
}
347+
#endif
348+
p = wcsrchr(argv0, SEP);
349+
}
350+
if (p != NULL) {
351+
n = p + 1 - argv0;
352+
#if SEP == '/' /* Special case for Unix filename syntax */
353+
if (n > 1)
354+
n--; /* Drop trailing separator */
355+
#endif /* Unix */
356+
}
357+
#endif /* All others */
358+
359+
return PyUnicode_FromWideChar(argv0, n);
360+
}
361+
264362
#ifdef __cplusplus
265363
}
266364
#endif

0 commit comments

Comments
 (0)