From 19760863623b636a63ccf649107d9504c6465a92 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 20 Dec 2017 01:41:59 +0100 Subject: [PATCH] bpo-32030: Cleanup pymain_main() (#4935) * Reorganize pymain_main() to make the code more flat * Clear configurations before pymain_update_sys_path() * Mark Py_FatalError() and _Py_FatalInitError() with _Py_NO_RETURN * Replace _PyMain.run_code variable with a new RUN_CODE() macro * Move _PyMain.cf into a local variable in pymain_run_python() --- Modules/main.c | 176 ++++++++++++++++++++++++------------------- Python/pylifecycle.c | 4 +- 2 files changed, 101 insertions(+), 79 deletions(-) diff --git a/Modules/main.c b/Modules/main.c index 59e535d6ae7..429ecbc767d 100644 --- a/Modules/main.c +++ b/Modules/main.c @@ -389,6 +389,7 @@ typedef struct { wchar_t *command; /* -c argument */ const wchar_t *module; /* -m argument */ _Py_OptList warning_options; /* -W options */ + _Py_OptList env_warning_options; /* PYTHONWARNINGS env var */ int print_help; /* -h, -? options */ int print_version; /* -V option */ int bytes_warning; /* Py_BytesWarningFlag, -b */ @@ -412,41 +413,47 @@ typedef struct { int legacy_windows_stdio; /* Py_LegacyWindowsStdioFlag, PYTHONLEGACYWINDOWSSTDIO */ #endif - _Py_OptList env_warning_options; /* PYTHONWARNINGS env var */ } _Py_CommandLineDetails; /* Structure used by Py_Main() to pass data to subfunctions */ typedef struct { - /* Exit status ("exit code") */ - int status; - PyCompilerFlags cf; - /* non-zero is stdin is a TTY or if -i option is used */ - int stdin_is_interactive; - _PyCoreConfig core_config; - _PyMainInterpreterConfig config; - _Py_CommandLineDetails cmdline; - PyObject *main_importer_path; - /* non-zero if filename, command (-c) or module (-m) is set - on the command line */ - int run_code; - /* Error message if a function failed */ - _PyInitError err; - + /* Input arguments */ int argc; int use_bytes_argv; char **bytes_argv; wchar_t **wchar_argv; + + /* Exit status or "exit code": result of pymain_main() */ + int status; + /* Error message if a function failed */ + _PyInitError err; + + _Py_CommandLineDetails cmdline; + /* non-zero is stdin is a TTY or if -i option is used */ + int stdin_is_interactive; + + _PyCoreConfig core_config; + _PyMainInterpreterConfig config; + + PyObject *main_importer_path; } _PyMain; /* .cmdline is initialized to zeros */ #define _PyMain_INIT \ {.core_config = _PyCoreConfig_INIT, \ .config = _PyMainInterpreterConfig_INIT, \ - .run_code = -1, \ .err = _Py_INIT_OK()} /* Note: _PyMain_INIT sets other fields to 0/NULL */ +/* Non-zero if filename, command (-c) or module (-m) is set + on the command line */ +#define RUN_CODE(pymain) \ + (pymain->cmdline.command != NULL \ + || pymain->cmdline.filename != NULL \ + || pymain->cmdline.module != NULL) + + static void pymain_optlist_clear(_Py_OptList *list) { @@ -526,7 +533,6 @@ pymain_clear_cmdline(_PyMain *pymain) cmdline->argv = NULL; } - static void pymain_clear_configs(_PyMain *pymain) { @@ -816,9 +822,6 @@ pymain_parse_cmdline_impl(_PyMain *pymain) cmdline->filename = cmdline->argv[_PyOS_optind]; } - pymain->run_code = (cmdline->command != NULL || cmdline->filename != NULL - || cmdline->module != NULL); - /* -c and -m options are exclusive */ assert(!(cmdline->command != NULL && cmdline->module != NULL)); @@ -1174,12 +1177,11 @@ pymain_get_program_name(_PyMain *pymain) static void pymain_header(_PyMain *pymain) { - /* TODO: Move this to _PyRun_PrepareMain */ if (Py_QuietFlag) { return; } - if (!Py_VerboseFlag && (pymain->run_code || !pymain->stdin_is_interactive)) { + if (!Py_VerboseFlag && (RUN_CODE(pymain) || !pymain->stdin_is_interactive)) { return; } @@ -1291,17 +1293,32 @@ config_init_argv(_PyMainInterpreterConfig *config, const _PyCoreConfig *core_con static int -pymain_update_sys_path(_PyMain *pymain) +pymain_init_path0(_PyMain *pymain, PyObject **path0) { if (pymain->main_importer_path != NULL) { /* Let pymain_run_main_from_importer() adjust sys.path[0] later */ + *path0 = NULL; return 0; } if (Py_IsolatedFlag) { + *path0 = NULL; return 0; } + *path0 = _PyPathConfig_ComputeArgv0(pymain->core_config.argc, + pymain->core_config.argv); + if (*path0 == NULL) { + pymain->err = _Py_INIT_NO_MEMORY(); + return -1; + } + return 0; +} + + +static int +pymain_update_sys_path(_PyMain *pymain, PyObject *path0) +{ /* Prepend argv[0] to sys.path. If argv[0] is a symlink, use the real path. */ PyObject *sys_path = PySys_GetObject("path"); @@ -1310,20 +1327,11 @@ pymain_update_sys_path(_PyMain *pymain) return -1; } - PyObject *path0 = _PyPathConfig_ComputeArgv0(pymain->core_config.argc, pymain->core_config.argv); - if (path0 == NULL) { - pymain->err = _Py_INIT_NO_MEMORY(); - return -1; - } - /* Prepend path0 to sys.path */ if (PyList_Insert(sys_path, 0, path0) < 0) { - Py_DECREF(path0); pymain->err = _Py_INIT_ERR("sys.path.insert(0, path0) failed"); return -1; } - Py_DECREF(path0); - return 0; } @@ -1357,7 +1365,7 @@ pymain_get_global_config(_PyMain *pymain) } -/* Set Py_XXX global configuration variables */ +/* Set Py_xxx global configuration variables */ static void pymain_set_global_config(_PyMain *pymain) { @@ -1396,7 +1404,7 @@ pymain_import_readline(_PyMain *pymain) if (Py_IsolatedFlag) { return; } - if (!Py_InspectFlag && pymain->run_code) { + if (!Py_InspectFlag && RUN_CODE(pymain)) { return; } if (!isatty(fileno(stdin))) { @@ -1464,13 +1472,13 @@ pymain_open_filename(_PyMain *pymain) static void -pymain_run_filename(_PyMain *pymain) +pymain_run_filename(_PyMain *pymain, PyCompilerFlags *cf) { _Py_CommandLineDetails *cmdline = &pymain->cmdline; if (cmdline->filename == NULL && pymain->stdin_is_interactive) { Py_InspectFlag = 0; /* do exit on SystemExit */ - pymain_run_startup(&pymain->cf); + pymain_run_startup(cf); pymain_run_interactive_hook(); } @@ -1490,12 +1498,12 @@ pymain_run_filename(_PyMain *pymain) fp = stdin; } - pymain->status = pymain_run_file(fp, cmdline->filename, &pymain->cf); + pymain->status = pymain_run_file(fp, cmdline->filename, cf); } static void -pymain_repl(_PyMain *pymain) +pymain_repl(_PyMain *pymain, PyCompilerFlags *cf) { /* Check this environment variable at the end, to give programs the opportunity to set it from Python. */ @@ -1503,15 +1511,14 @@ pymain_repl(_PyMain *pymain) Py_InspectFlag = 1; } - if (!(Py_InspectFlag && pymain->stdin_is_interactive - && pymain->run_code)) { + if (!(Py_InspectFlag && pymain->stdin_is_interactive && RUN_CODE(pymain))) { return; } Py_InspectFlag = 0; pymain_run_interactive_hook(); - int res = PyRun_AnyFileFlags(stdin, "", &pymain->cf); + int res = PyRun_AnyFileFlags(stdin, "", cf); pymain->status = (res != 0); } @@ -1966,6 +1973,10 @@ pymain_read_conf_impl(_PyMain *pymain) return res; } + if (pymain_init_core_argv(pymain) < 0) { + return -1; + } + /* Set Py_IgnoreEnvironmentFlag for Py_GETENV() */ Py_IgnoreEnvironmentFlag = pymain->core_config.ignore_environment; @@ -1973,10 +1984,6 @@ pymain_read_conf_impl(_PyMain *pymain) return -1; } - if (pymain_init_core_argv(pymain) < 0) { - return -1; - } - _PyInitError err = _PyCoreConfig_Read(&pymain->core_config); if (_Py_INIT_FAILED(err)) { pymain->err = err; @@ -1986,6 +1993,8 @@ pymain_read_conf_impl(_PyMain *pymain) } +/* Read the configuration, but initialize also the LC_CTYPE locale: + enable UTF-8 mode (PEP 540) and/or coerce the C locale (PEP 538) */ static int pymain_read_conf(_PyMain *pymain) { @@ -2426,9 +2435,21 @@ pymain_init_python_main(_PyMain *pymain) pymain->main_importer_path = pymain_get_importer(pymain->cmdline.filename); } - if (pymain_update_sys_path(pymain) < 0) { + PyObject *path0; + if (pymain_init_path0(pymain, &path0) < 0) { return -1; } + + pymain_clear_configs(pymain); + + if (path0 != NULL) { + if (pymain_update_sys_path(pymain, path0) < 0) { + Py_DECREF(path0); + return -1; + } + Py_DECREF(path0); + } + return 0; } @@ -2436,21 +2457,22 @@ pymain_init_python_main(_PyMain *pymain) static void pymain_run_python(_PyMain *pymain) { + PyCompilerFlags cf = {.cf_flags = 0}; _Py_CommandLineDetails *cmdline = &pymain->cmdline; pymain_header(pymain); pymain_import_readline(pymain); if (cmdline->command) { - pymain->status = pymain_run_command(cmdline->command, &pymain->cf); + pymain->status = pymain_run_command(cmdline->command, &cf); } else if (cmdline->module) { pymain->status = (pymain_run_module(cmdline->module, 1) != 0); } else { - pymain_run_filename(pymain); + pymain_run_filename(pymain, &cf); } - pymain_repl(pymain); + pymain_repl(pymain, &cf); } @@ -2476,59 +2498,37 @@ pymain_init(_PyMain *pymain) static int -pymain_impl(_PyMain *pymain) +pymain_init_cmdline(_PyMain *pymain) { pymain->err = _PyRuntime_Initialize(); if (_Py_INIT_FAILED(pymain->err)) { return -1; } - /* Read the configuration, but initialize also the LC_CTYPE locale: - enable UTF-8 mode (PEP 540) and/or coerce the C locale (PEP 538) */ int res = pymain_read_conf(pymain); if (res < 0) { return -1; } if (res > 0) { /* --help or --version command: we are done */ - return 0; + return 1; } _Py_CommandLineDetails *cmdline = &pymain->cmdline; if (cmdline->print_help) { pymain_usage(0, cmdline->argv[0]); - return 0; + return 1; } if (cmdline->print_version) { printf("Python %s\n", (cmdline->print_version >= 2) ? Py_GetVersion() : PY_VERSION); - return 0; + return 1; } /* For Py_GetArgcArgv(). Cleared by pymain_free(). */ orig_argc = pymain->argc; orig_argv = cmdline->argv; - - res = pymain_init_python_core(pymain); - if (res < 0) { - return -1; - } - - res = pymain_init_python_main(pymain); - if (res < 0) { - return -1; - } - - pymain_clear_configs(pymain); - - pymain_run_python(pymain); - - if (Py_FinalizeEx() < 0) { - /* Value unlikely to be confused with a non-error exit status or - other special meaning */ - pymain->status = 120; - } return 0; } @@ -2538,9 +2538,31 @@ pymain_main(_PyMain *pymain) { pymain_init(pymain); - if (pymain_impl(pymain) < 0) { + int res = pymain_init_cmdline(pymain); + if (res < 0) { _Py_FatalInitError(pymain->err); } + if (res == 1) { + goto done; + } + + if (pymain_init_python_core(pymain) < 0) { + _Py_FatalInitError(pymain->err); + } + + if (pymain_init_python_main(pymain) < 0) { + _Py_FatalInitError(pymain->err); + } + + pymain_run_python(pymain); + + if (Py_FinalizeEx() < 0) { + /* Value unlikely to be confused with a non-error exit status or + other special meaning */ + pymain->status = 120; + } + +done: pymain_free(pymain); return pymain->status; diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 678fbb63cc8..090694f7cae 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -2003,13 +2003,13 @@ exit: } } -void +void _Py_NO_RETURN Py_FatalError(const char *msg) { fatal_error(NULL, msg, -1); } -void +void _Py_NO_RETURN _Py_FatalInitError(_PyInitError err) { /* On "user" error: exit with status 1.