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:
parent
bcfbbd7046
commit
12083284c5
|
@ -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
|
||||||
|
|
|
@ -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``.
|
123
Modules/main.c
123
Modules/main.c
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue