bpo-36763: _Py_RunMain() doesn't call Py_Exit() anymore (GH-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().
This commit is contained in:
Victor Stinner 2019-05-17 23:05:29 +02:00 committed by GitHub
parent bcfbbd7046
commit 12083284c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 117 additions and 63 deletions

View File

@ -100,6 +100,8 @@ PyAPI_FUNC(_PyInitError) _Py_PreInitializeFromCoreConfig(
const _PyCoreConfig *coreconfig, const _PyCoreConfig *coreconfig,
const _PyArgv *args); const _PyArgv *args);
PyAPI_FUNC(int) _Py_HandleSystemExit(int *exitcode_p);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -0,0 +1,3 @@
``Py_Main()`` now returns the exitcode rather than calling
``Py_Exit(exitcode)`` when calling ``PyErr_Print()`` if the current
exception type is ``SystemExit``.

View File

@ -94,8 +94,34 @@ stdin_is_interactive(const _PyCoreConfig *config)
} }
static PyObject * /* Display the current Python exception and return an exitcode */
pymain_get_importer(const wchar_t *filename) static int
pymain_err_print(int *exitcode_p)
{
int exitcode;
if (_Py_HandleSystemExit(&exitcode)) {
*exitcode_p = exitcode;
return 1;
}
PyErr_Print();
return 0;
}
static int
pymain_exit_err_print(void)
{
int exitcode = 1;
pymain_err_print(&exitcode);
return exitcode;
}
/* Write an exitcode into *exitcode and return 1 if we have to exit Python.
Return 0 otherwise. */
static int
pymain_get_importer(const wchar_t *filename, PyObject **importer_p, int *exitcode)
{ {
PyObject *sys_path0 = NULL, *importer; PyObject *sys_path0 = NULL, *importer;
@ -112,17 +138,18 @@ pymain_get_importer(const wchar_t *filename)
if (importer == Py_None) { if (importer == Py_None) {
Py_DECREF(sys_path0); Py_DECREF(sys_path0);
Py_DECREF(importer); Py_DECREF(importer);
return NULL; return 0;
} }
Py_DECREF(importer); Py_DECREF(importer);
return sys_path0; *importer_p = sys_path0;
return 0;
error: error:
Py_XDECREF(sys_path0); Py_XDECREF(sys_path0);
PySys_WriteStderr("Failed checking if argv[0] is an import path entry\n"); PySys_WriteStderr("Failed checking if argv[0] is an import path entry\n");
PyErr_Print(); return pymain_err_print(exitcode);
return NULL;
} }
@ -217,8 +244,7 @@ pymain_run_command(wchar_t *command, PyCompilerFlags *cf)
error: error:
PySys_WriteStderr("Unable to decode the command from the command line:\n"); PySys_WriteStderr("Unable to decode the command from the command line:\n");
PyErr_Print(); return pymain_exit_err_print();
return 1;
} }
@ -229,44 +255,37 @@ pymain_run_module(const wchar_t *modname, int set_argv0)
runpy = PyImport_ImportModule("runpy"); runpy = PyImport_ImportModule("runpy");
if (runpy == NULL) { if (runpy == NULL) {
fprintf(stderr, "Could not import runpy module\n"); fprintf(stderr, "Could not import runpy module\n");
PyErr_Print(); return pymain_exit_err_print();
return -1;
} }
runmodule = PyObject_GetAttrString(runpy, "_run_module_as_main"); runmodule = PyObject_GetAttrString(runpy, "_run_module_as_main");
if (runmodule == NULL) { if (runmodule == NULL) {
fprintf(stderr, "Could not access runpy._run_module_as_main\n"); fprintf(stderr, "Could not access runpy._run_module_as_main\n");
PyErr_Print();
Py_DECREF(runpy); Py_DECREF(runpy);
return -1; return pymain_exit_err_print();
} }
module = PyUnicode_FromWideChar(modname, wcslen(modname)); module = PyUnicode_FromWideChar(modname, wcslen(modname));
if (module == NULL) { if (module == NULL) {
fprintf(stderr, "Could not convert module name to unicode\n"); fprintf(stderr, "Could not convert module name to unicode\n");
PyErr_Print();
Py_DECREF(runpy); Py_DECREF(runpy);
Py_DECREF(runmodule); Py_DECREF(runmodule);
return -1; return pymain_exit_err_print();
} }
runargs = Py_BuildValue("(Oi)", module, set_argv0); runargs = Py_BuildValue("(Oi)", module, set_argv0);
if (runargs == NULL) { if (runargs == NULL) {
fprintf(stderr, fprintf(stderr,
"Could not create arguments for runpy._run_module_as_main\n"); "Could not create arguments for runpy._run_module_as_main\n");
PyErr_Print();
Py_DECREF(runpy); Py_DECREF(runpy);
Py_DECREF(runmodule); Py_DECREF(runmodule);
Py_DECREF(module); Py_DECREF(module);
return -1; return pymain_exit_err_print();
} }
result = PyObject_Call(runmodule, runargs, NULL); result = PyObject_Call(runmodule, runargs, NULL);
if (result == NULL) {
PyErr_Print();
}
Py_DECREF(runpy); Py_DECREF(runpy);
Py_DECREF(runmodule); Py_DECREF(runmodule);
Py_DECREF(module); Py_DECREF(module);
Py_DECREF(runargs); Py_DECREF(runargs);
if (result == NULL) { if (result == NULL) {
return -1; return pymain_exit_err_print();
} }
Py_DECREF(result); Py_DECREF(result);
return 0; return 0;
@ -315,9 +334,8 @@ pymain_run_file(_PyCoreConfig *config, PyCompilerFlags *cf)
/* call pending calls like signal handlers (SIGINT) */ /* call pending calls like signal handlers (SIGINT) */
if (Py_MakePendingCalls() == -1) { if (Py_MakePendingCalls() == -1) {
PyErr_Print();
fclose(fp); fclose(fp);
return 1; return pymain_exit_err_print();
} }
PyObject *unicode, *bytes = NULL; PyObject *unicode, *bytes = NULL;
@ -343,34 +361,36 @@ pymain_run_file(_PyCoreConfig *config, PyCompilerFlags *cf)
} }
static void static int
pymain_run_startup(_PyCoreConfig *config, PyCompilerFlags *cf) pymain_run_startup(_PyCoreConfig *config, PyCompilerFlags *cf, int *exitcode)
{ {
const char *startup = _Py_GetEnv(config->use_environment, "PYTHONSTARTUP"); const char *startup = _Py_GetEnv(config->use_environment, "PYTHONSTARTUP");
if (startup == NULL) { if (startup == NULL) {
return; return 0;
} }
FILE *fp = _Py_fopen(startup, "r"); FILE *fp = _Py_fopen(startup, "r");
if (fp == NULL) { if (fp == NULL) {
int save_errno = errno; int save_errno = errno;
PySys_WriteStderr("Could not open PYTHONSTARTUP\n"); PySys_WriteStderr("Could not open PYTHONSTARTUP\n");
errno = save_errno;
PyErr_SetFromErrnoWithFilename(PyExc_OSError, errno = save_errno;
startup); PyErr_SetFromErrnoWithFilename(PyExc_OSError, startup);
PyErr_Print();
return; return pymain_err_print(exitcode);
} }
(void) PyRun_SimpleFileExFlags(fp, startup, 0, cf); (void) PyRun_SimpleFileExFlags(fp, startup, 0, cf);
PyErr_Clear(); PyErr_Clear();
fclose(fp); fclose(fp);
return 0;
} }
static void /* Write an exitcode into *exitcode and return 1 if we have to exit Python.
pymain_run_interactive_hook(void) Return 0 otherwise. */
static int
pymain_run_interactive_hook(int *exitcode)
{ {
PyObject *sys, *hook, *result; PyObject *sys, *hook, *result;
sys = PyImport_ImportModule("sys"); sys = PyImport_ImportModule("sys");
@ -382,7 +402,7 @@ pymain_run_interactive_hook(void)
Py_DECREF(sys); Py_DECREF(sys);
if (hook == NULL) { if (hook == NULL) {
PyErr_Clear(); PyErr_Clear();
return; return 0;
} }
result = _PyObject_CallNoArg(hook); result = _PyObject_CallNoArg(hook);
@ -392,11 +412,11 @@ pymain_run_interactive_hook(void)
} }
Py_DECREF(result); Py_DECREF(result);
return; return 0;
error: error:
PySys_WriteStderr("Failed calling sys.__interactivehook__\n"); PySys_WriteStderr("Failed calling sys.__interactivehook__\n");
PyErr_Print(); return pymain_err_print(exitcode);
} }
@ -406,14 +426,20 @@ pymain_run_stdin(_PyCoreConfig *config, PyCompilerFlags *cf)
if (stdin_is_interactive(config)) { if (stdin_is_interactive(config)) {
config->inspect = 0; config->inspect = 0;
Py_InspectFlag = 0; /* do exit on SystemExit */ Py_InspectFlag = 0; /* do exit on SystemExit */
pymain_run_startup(config, cf);
pymain_run_interactive_hook(); int exitcode;
if (pymain_run_startup(config, cf, &exitcode)) {
return exitcode;
}
if (pymain_run_interactive_hook(&exitcode)) {
return exitcode;
}
} }
/* call pending calls like signal handlers (SIGINT) */ /* call pending calls like signal handlers (SIGINT) */
if (Py_MakePendingCalls() == -1) { if (Py_MakePendingCalls() == -1) {
PyErr_Print(); return pymain_exit_err_print();
return 1;
} }
int run = PyRun_AnyFileExFlags(stdin, "<stdin>", 0, cf); int run = PyRun_AnyFileExFlags(stdin, "<stdin>", 0, cf);
@ -437,7 +463,9 @@ pymain_repl(_PyCoreConfig *config, PyCompilerFlags *cf, int *exitcode)
config->inspect = 0; config->inspect = 0;
Py_InspectFlag = 0; Py_InspectFlag = 0;
pymain_run_interactive_hook(); if (pymain_run_interactive_hook(exitcode)) {
return;
}
int res = PyRun_AnyFileFlags(stdin, "<stdin>", cf); int res = PyRun_AnyFileFlags(stdin, "<stdin>", cf);
*exitcode = (res != 0); *exitcode = (res != 0);
@ -457,8 +485,11 @@ pymain_run_python(int *exitcode)
__main__.py, main_importer_path is set to filename and will be __main__.py, main_importer_path is set to filename and will be
prepended to sys.path. prepended to sys.path.
Otherwise, main_importer_path is set to NULL. */ Otherwise, main_importer_path is left unchanged. */
main_importer_path = pymain_get_importer(config->run_filename); if (pymain_get_importer(config->run_filename, &main_importer_path,
exitcode)) {
return;
}
} }
if (main_importer_path != NULL) { if (main_importer_path != NULL) {
@ -491,11 +522,10 @@ pymain_run_python(int *exitcode)
*exitcode = pymain_run_command(config->run_command, &cf); *exitcode = pymain_run_command(config->run_command, &cf);
} }
else if (config->run_module) { else if (config->run_module) {
*exitcode = (pymain_run_module(config->run_module, 1) != 0); *exitcode = pymain_run_module(config->run_module, 1);
} }
else if (main_importer_path != NULL) { else if (main_importer_path != NULL) {
int sts = pymain_run_module(L"__main__", 0); *exitcode = pymain_run_module(L"__main__", 0);
*exitcode = (sts != 0);
} }
else if (config->run_filename != NULL) { else if (config->run_filename != NULL) {
*exitcode = pymain_run_file(config, &cf); *exitcode = pymain_run_file(config, &cf);
@ -508,8 +538,7 @@ pymain_run_python(int *exitcode)
goto done; goto done;
error: error:
PyErr_Print(); *exitcode = pymain_exit_err_print();
*exitcode = 1;
done: done:
Py_XDECREF(main_importer_path); Py_XDECREF(main_importer_path);

View File

@ -585,23 +585,31 @@ print_error_text(PyObject *f, int offset, PyObject *text_obj)
PyFile_WriteString("^\n", f); PyFile_WriteString("^\n", f);
} }
static void
handle_system_exit(void)
{
PyObject *exception, *value, *tb;
int exitcode = 0;
int
_Py_HandleSystemExit(int *exitcode_p)
{
int inspect = _PyInterpreterState_GET_UNSAFE()->core_config.inspect; int inspect = _PyInterpreterState_GET_UNSAFE()->core_config.inspect;
if (inspect) { if (inspect) {
/* Don't exit if -i flag was given. This flag is set to 0 /* Don't exit if -i flag was given. This flag is set to 0
* when entering interactive mode for inspecting. */ * when entering interactive mode for inspecting. */
return; return 0;
} }
if (!PyErr_ExceptionMatches(PyExc_SystemExit)) {
return 0;
}
PyObject *exception, *value, *tb;
PyErr_Fetch(&exception, &value, &tb); PyErr_Fetch(&exception, &value, &tb);
fflush(stdout); fflush(stdout);
if (value == NULL || value == Py_None)
int exitcode = 0;
if (value == NULL || value == Py_None) {
goto done; goto done;
}
if (PyExceptionInstance_Check(value)) { if (PyExceptionInstance_Check(value)) {
/* The error code should be in the `code' attribute. */ /* The error code should be in the `code' attribute. */
_Py_IDENTIFIER(code); _Py_IDENTIFIER(code);
@ -615,8 +623,10 @@ handle_system_exit(void)
/* If we failed to dig out the 'code' attribute, /* If we failed to dig out the 'code' attribute,
just let the else clause below print the error. */ just let the else clause below print the error. */
} }
if (PyLong_Check(value))
if (PyLong_Check(value)) {
exitcode = (int)PyLong_AsLong(value); exitcode = (int)PyLong_AsLong(value);
}
else { else {
PyObject *sys_stderr = _PySys_GetObjectId(&PyId_stderr); PyObject *sys_stderr = _PySys_GetObjectId(&PyId_stderr);
/* We clear the exception here to avoid triggering the assertion /* We clear the exception here to avoid triggering the assertion
@ -633,6 +643,7 @@ handle_system_exit(void)
PySys_WriteStderr("\n"); PySys_WriteStderr("\n");
exitcode = 1; exitcode = 1;
} }
done: done:
/* Restore and clear the exception info, in order to properly decref /* Restore and clear the exception info, in order to properly decref
* the exception, value, and traceback. If we just exit instead, * the exception, value, and traceback. If we just exit instead,
@ -641,18 +652,28 @@ handle_system_exit(void)
*/ */
PyErr_Restore(exception, value, tb); PyErr_Restore(exception, value, tb);
PyErr_Clear(); PyErr_Clear();
Py_Exit(exitcode); *exitcode_p = exitcode;
/* NOTREACHED */ return 1;
} }
static void
handle_system_exit(void)
{
int exitcode;
if (_Py_HandleSystemExit(&exitcode)) {
Py_Exit(exitcode);
}
}
void void
PyErr_PrintEx(int set_sys_last_vars) PyErr_PrintEx(int set_sys_last_vars)
{ {
PyObject *exception, *v, *tb, *hook; PyObject *exception, *v, *tb, *hook;
if (PyErr_ExceptionMatches(PyExc_SystemExit)) { handle_system_exit();
handle_system_exit();
}
PyErr_Fetch(&exception, &v, &tb); PyErr_Fetch(&exception, &v, &tb);
if (exception == NULL) if (exception == NULL)
return; return;
@ -686,10 +707,9 @@ PyErr_PrintEx(int set_sys_last_vars)
stack[2] = tb; stack[2] = tb;
result = _PyObject_FastCall(hook, stack, 3); result = _PyObject_FastCall(hook, stack, 3);
if (result == NULL) { if (result == NULL) {
handle_system_exit();
PyObject *exception2, *v2, *tb2; PyObject *exception2, *v2, *tb2;
if (PyErr_ExceptionMatches(PyExc_SystemExit)) {
handle_system_exit();
}
PyErr_Fetch(&exception2, &v2, &tb2); PyErr_Fetch(&exception2, &v2, &tb2);
PyErr_NormalizeException(&exception2, &v2, &tb2); PyErr_NormalizeException(&exception2, &v2, &tb2);
/* It should not be possible for exception2 or v2 /* It should not be possible for exception2 or v2