Skip to content

Commit 1208328

Browse files
authored
bpo-36763: _Py_RunMain() doesn't call Py_Exit() anymore (pythonGH-13390)
Py_Main() and _Py_RunMain() now return the exitcode rather than calling Py_Exit(exitcode) when calling PyErr_Print() if the current exception type is SystemExit. * Add _Py_HandleSystemExit(). * Add pymain_exit_err_print(). * Add pymain_exit_print().
1 parent bcfbbd7 commit 1208328

File tree

4 files changed

+116
-62
lines changed

4 files changed

+116
-62
lines changed

Include/internal/pycore_pylifecycle.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ PyAPI_FUNC(_PyInitError) _Py_PreInitializeFromCoreConfig(
100100
const _PyCoreConfig *coreconfig,
101101
const _PyArgv *args);
102102

103+
PyAPI_FUNC(int) _Py_HandleSystemExit(int *exitcode_p);
104+
103105
#ifdef __cplusplus
104106
}
105107
#endif
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
``Py_Main()`` now returns the exitcode rather than calling
2+
``Py_Exit(exitcode)`` when calling ``PyErr_Print()`` if the current
3+
exception type is ``SystemExit``.

Modules/main.c

Lines changed: 75 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,34 @@ stdin_is_interactive(const _PyCoreConfig *config)
9494
}
9595

9696

97-
static PyObject *
98-
pymain_get_importer(const wchar_t *filename)
97+
/* Display the current Python exception and return an exitcode */
98+
static int
99+
pymain_err_print(int *exitcode_p)
100+
{
101+
int exitcode;
102+
if (_Py_HandleSystemExit(&exitcode)) {
103+
*exitcode_p = exitcode;
104+
return 1;
105+
}
106+
107+
PyErr_Print();
108+
return 0;
109+
}
110+
111+
112+
static int
113+
pymain_exit_err_print(void)
114+
{
115+
int exitcode = 1;
116+
pymain_err_print(&exitcode);
117+
return exitcode;
118+
}
119+
120+
121+
/* Write an exitcode into *exitcode and return 1 if we have to exit Python.
122+
Return 0 otherwise. */
123+
static int
124+
pymain_get_importer(const wchar_t *filename, PyObject **importer_p, int *exitcode)
99125
{
100126
PyObject *sys_path0 = NULL, *importer;
101127

@@ -112,17 +138,18 @@ pymain_get_importer(const wchar_t *filename)
112138
if (importer == Py_None) {
113139
Py_DECREF(sys_path0);
114140
Py_DECREF(importer);
115-
return NULL;
141+
return 0;
116142
}
117143

118144
Py_DECREF(importer);
119-
return sys_path0;
145+
*importer_p = sys_path0;
146+
return 0;
120147

121148
error:
122149
Py_XDECREF(sys_path0);
150+
123151
PySys_WriteStderr("Failed checking if argv[0] is an import path entry\n");
124-
PyErr_Print();
125-
return NULL;
152+
return pymain_err_print(exitcode);
126153
}
127154

128155

@@ -217,8 +244,7 @@ pymain_run_command(wchar_t *command, PyCompilerFlags *cf)
217244

218245
error:
219246
PySys_WriteStderr("Unable to decode the command from the command line:\n");
220-
PyErr_Print();
221-
return 1;
247+
return pymain_exit_err_print();
222248
}
223249

224250

@@ -229,44 +255,37 @@ pymain_run_module(const wchar_t *modname, int set_argv0)
229255
runpy = PyImport_ImportModule("runpy");
230256
if (runpy == NULL) {
231257
fprintf(stderr, "Could not import runpy module\n");
232-
PyErr_Print();
233-
return -1;
258+
return pymain_exit_err_print();
234259
}
235260
runmodule = PyObject_GetAttrString(runpy, "_run_module_as_main");
236261
if (runmodule == NULL) {
237262
fprintf(stderr, "Could not access runpy._run_module_as_main\n");
238-
PyErr_Print();
239263
Py_DECREF(runpy);
240-
return -1;
264+
return pymain_exit_err_print();
241265
}
242266
module = PyUnicode_FromWideChar(modname, wcslen(modname));
243267
if (module == NULL) {
244268
fprintf(stderr, "Could not convert module name to unicode\n");
245-
PyErr_Print();
246269
Py_DECREF(runpy);
247270
Py_DECREF(runmodule);
248-
return -1;
271+
return pymain_exit_err_print();
249272
}
250273
runargs = Py_BuildValue("(Oi)", module, set_argv0);
251274
if (runargs == NULL) {
252275
fprintf(stderr,
253276
"Could not create arguments for runpy._run_module_as_main\n");
254-
PyErr_Print();
255277
Py_DECREF(runpy);
256278
Py_DECREF(runmodule);
257279
Py_DECREF(module);
258-
return -1;
280+
return pymain_exit_err_print();
259281
}
260282
result = PyObject_Call(runmodule, runargs, NULL);
261-
if (result == NULL) {
262-
PyErr_Print();
263-
}
264283
Py_DECREF(runpy);
265284
Py_DECREF(runmodule);
266285
Py_DECREF(module);
267286
Py_DECREF(runargs);
268287
if (result == NULL) {
269-
return -1;
288+
return pymain_exit_err_print();
270289
}
271290
Py_DECREF(result);
272291
return 0;
@@ -315,9 +334,8 @@ pymain_run_file(_PyCoreConfig *config, PyCompilerFlags *cf)
315334

316335
/* call pending calls like signal handlers (SIGINT) */
317336
if (Py_MakePendingCalls() == -1) {
318-
PyErr_Print();
319337
fclose(fp);
320-
return 1;
338+
return pymain_exit_err_print();
321339
}
322340

323341
PyObject *unicode, *bytes = NULL;
@@ -343,34 +361,36 @@ pymain_run_file(_PyCoreConfig *config, PyCompilerFlags *cf)
343361
}
344362

345363

346-
static void
347-
pymain_run_startup(_PyCoreConfig *config, PyCompilerFlags *cf)
364+
static int
365+
pymain_run_startup(_PyCoreConfig *config, PyCompilerFlags *cf, int *exitcode)
348366
{
349367
const char *startup = _Py_GetEnv(config->use_environment, "PYTHONSTARTUP");
350368
if (startup == NULL) {
351-
return;
369+
return 0;
352370
}
353371

354372
FILE *fp = _Py_fopen(startup, "r");
355373
if (fp == NULL) {
356374
int save_errno = errno;
357375
PySys_WriteStderr("Could not open PYTHONSTARTUP\n");
376+
358377
errno = save_errno;
378+
PyErr_SetFromErrnoWithFilename(PyExc_OSError, startup);
359379

360-
PyErr_SetFromErrnoWithFilename(PyExc_OSError,
361-
startup);
362-
PyErr_Print();
363-
return;
380+
return pymain_err_print(exitcode);
364381
}
365382

366383
(void) PyRun_SimpleFileExFlags(fp, startup, 0, cf);
367384
PyErr_Clear();
368385
fclose(fp);
386+
return 0;
369387
}
370388

371389

372-
static void
373-
pymain_run_interactive_hook(void)
390+
/* Write an exitcode into *exitcode and return 1 if we have to exit Python.
391+
Return 0 otherwise. */
392+
static int
393+
pymain_run_interactive_hook(int *exitcode)
374394
{
375395
PyObject *sys, *hook, *result;
376396
sys = PyImport_ImportModule("sys");
@@ -382,7 +402,7 @@ pymain_run_interactive_hook(void)
382402
Py_DECREF(sys);
383403
if (hook == NULL) {
384404
PyErr_Clear();
385-
return;
405+
return 0;
386406
}
387407

388408
result = _PyObject_CallNoArg(hook);
@@ -392,11 +412,11 @@ pymain_run_interactive_hook(void)
392412
}
393413
Py_DECREF(result);
394414

395-
return;
415+
return 0;
396416

397417
error:
398418
PySys_WriteStderr("Failed calling sys.__interactivehook__\n");
399-
PyErr_Print();
419+
return pymain_err_print(exitcode);
400420
}
401421

402422

@@ -406,14 +426,20 @@ pymain_run_stdin(_PyCoreConfig *config, PyCompilerFlags *cf)
406426
if (stdin_is_interactive(config)) {
407427
config->inspect = 0;
408428
Py_InspectFlag = 0; /* do exit on SystemExit */
409-
pymain_run_startup(config, cf);
410-
pymain_run_interactive_hook();
429+
430+
int exitcode;
431+
if (pymain_run_startup(config, cf, &exitcode)) {
432+
return exitcode;
433+
}
434+
435+
if (pymain_run_interactive_hook(&exitcode)) {
436+
return exitcode;
437+
}
411438
}
412439

413440
/* call pending calls like signal handlers (SIGINT) */
414441
if (Py_MakePendingCalls() == -1) {
415-
PyErr_Print();
416-
return 1;
442+
return pymain_exit_err_print();
417443
}
418444

419445
int run = PyRun_AnyFileExFlags(stdin, "<stdin>", 0, cf);
@@ -437,7 +463,9 @@ pymain_repl(_PyCoreConfig *config, PyCompilerFlags *cf, int *exitcode)
437463

438464
config->inspect = 0;
439465
Py_InspectFlag = 0;
440-
pymain_run_interactive_hook();
466+
if (pymain_run_interactive_hook(exitcode)) {
467+
return;
468+
}
441469

442470
int res = PyRun_AnyFileFlags(stdin, "<stdin>", cf);
443471
*exitcode = (res != 0);
@@ -457,8 +485,11 @@ pymain_run_python(int *exitcode)
457485
__main__.py, main_importer_path is set to filename and will be
458486
prepended to sys.path.
459487
460-
Otherwise, main_importer_path is set to NULL. */
461-
main_importer_path = pymain_get_importer(config->run_filename);
488+
Otherwise, main_importer_path is left unchanged. */
489+
if (pymain_get_importer(config->run_filename, &main_importer_path,
490+
exitcode)) {
491+
return;
492+
}
462493
}
463494

464495
if (main_importer_path != NULL) {
@@ -491,11 +522,10 @@ pymain_run_python(int *exitcode)
491522
*exitcode = pymain_run_command(config->run_command, &cf);
492523
}
493524
else if (config->run_module) {
494-
*exitcode = (pymain_run_module(config->run_module, 1) != 0);
525+
*exitcode = pymain_run_module(config->run_module, 1);
495526
}
496527
else if (main_importer_path != NULL) {
497-
int sts = pymain_run_module(L"__main__", 0);
498-
*exitcode = (sts != 0);
528+
*exitcode = pymain_run_module(L"__main__", 0);
499529
}
500530
else if (config->run_filename != NULL) {
501531
*exitcode = pymain_run_file(config, &cf);
@@ -508,8 +538,7 @@ pymain_run_python(int *exitcode)
508538
goto done;
509539

510540
error:
511-
PyErr_Print();
512-
*exitcode = 1;
541+
*exitcode = pymain_exit_err_print();
513542

514543
done:
515544
Py_XDECREF(main_importer_path);

Python/pythonrun.c

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -585,23 +585,31 @@ print_error_text(PyObject *f, int offset, PyObject *text_obj)
585585
PyFile_WriteString("^\n", f);
586586
}
587587

588-
static void
589-
handle_system_exit(void)
590-
{
591-
PyObject *exception, *value, *tb;
592-
int exitcode = 0;
593588

589+
int
590+
_Py_HandleSystemExit(int *exitcode_p)
591+
{
594592
int inspect = _PyInterpreterState_GET_UNSAFE()->core_config.inspect;
595593
if (inspect) {
596594
/* Don't exit if -i flag was given. This flag is set to 0
597595
* when entering interactive mode for inspecting. */
598-
return;
596+
return 0;
599597
}
600598

599+
if (!PyErr_ExceptionMatches(PyExc_SystemExit)) {
600+
return 0;
601+
}
602+
603+
PyObject *exception, *value, *tb;
601604
PyErr_Fetch(&exception, &value, &tb);
605+
602606
fflush(stdout);
603-
if (value == NULL || value == Py_None)
607+
608+
int exitcode = 0;
609+
if (value == NULL || value == Py_None) {
604610
goto done;
611+
}
612+
605613
if (PyExceptionInstance_Check(value)) {
606614
/* The error code should be in the `code' attribute. */
607615
_Py_IDENTIFIER(code);
@@ -615,8 +623,10 @@ handle_system_exit(void)
615623
/* If we failed to dig out the 'code' attribute,
616624
just let the else clause below print the error. */
617625
}
618-
if (PyLong_Check(value))
626+
627+
if (PyLong_Check(value)) {
619628
exitcode = (int)PyLong_AsLong(value);
629+
}
620630
else {
621631
PyObject *sys_stderr = _PySys_GetObjectId(&PyId_stderr);
622632
/* We clear the exception here to avoid triggering the assertion
@@ -633,6 +643,7 @@ handle_system_exit(void)
633643
PySys_WriteStderr("\n");
634644
exitcode = 1;
635645
}
646+
636647
done:
637648
/* Restore and clear the exception info, in order to properly decref
638649
* the exception, value, and traceback. If we just exit instead,
@@ -641,18 +652,28 @@ handle_system_exit(void)
641652
*/
642653
PyErr_Restore(exception, value, tb);
643654
PyErr_Clear();
644-
Py_Exit(exitcode);
645-
/* NOTREACHED */
655+
*exitcode_p = exitcode;
656+
return 1;
646657
}
647658

659+
660+
static void
661+
handle_system_exit(void)
662+
{
663+
int exitcode;
664+
if (_Py_HandleSystemExit(&exitcode)) {
665+
Py_Exit(exitcode);
666+
}
667+
}
668+
669+
648670
void
649671
PyErr_PrintEx(int set_sys_last_vars)
650672
{
651673
PyObject *exception, *v, *tb, *hook;
652674

653-
if (PyErr_ExceptionMatches(PyExc_SystemExit)) {
654-
handle_system_exit();
655-
}
675+
handle_system_exit();
676+
656677
PyErr_Fetch(&exception, &v, &tb);
657678
if (exception == NULL)
658679
return;
@@ -686,10 +707,9 @@ PyErr_PrintEx(int set_sys_last_vars)
686707
stack[2] = tb;
687708
result = _PyObject_FastCall(hook, stack, 3);
688709
if (result == NULL) {
710+
handle_system_exit();
711+
689712
PyObject *exception2, *v2, *tb2;
690-
if (PyErr_ExceptionMatches(PyExc_SystemExit)) {
691-
handle_system_exit();
692-
}
693713
PyErr_Fetch(&exception2, &v2, &tb2);
694714
PyErr_NormalizeException(&exception2, &v2, &tb2);
695715
/* It should not be possible for exception2 or v2

0 commit comments

Comments
 (0)