diff options
author | Victor Stinner <victor.stinner@gmail.com> | 2017-11-16 02:11:45 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-11-16 02:11:45 (GMT) |
commit | a7368ac6360246b1ef7f8f152963c2362d272183 (patch) | |
tree | 4fb2375f4d4aa4287f4d7688631111512d9e4446 /Modules/main.c | |
parent | f7e5b56c37eb859e225e886c79c5d742c567ee95 (diff) | |
download | cpython-a7368ac6360246b1ef7f8f152963c2362d272183.zip cpython-a7368ac6360246b1ef7f8f152963c2362d272183.tar.gz cpython-a7368ac6360246b1ef7f8f152963c2362d272183.tar.bz2 |
bpo-32030: Enhance Py_Main() (#4412)
Parse more env vars in Py_Main():
* Add more options to _PyCoreConfig:
* faulthandler
* tracemalloc
* importtime
* Move code to parse environment variables from _Py_InitializeCore()
to Py_Main(). This change fixes a regression from Python 3.6:
PYTHONUNBUFFERED is now read before calling pymain_init_stdio().
* _PyFaulthandler_Init() and _PyTraceMalloc_Init() now take an
argument to decide if the module has to be enabled at startup.
* tracemalloc_start() is now responsible to check the maximum number
of frames.
Other changes:
* Cleanup Py_Main():
* Rename some pymain_xxx() subfunctions
* Add pymain_run_python() subfunction
* Cleanup Py_NewInterpreter()
* _PyInterpreterState_Enable() now reports failure
* init_hash_secret() now considers pyurandom() failure as an "user
error": don't fail with abort().
* pymain_optlist_append() and pymain_strdup() now sets err on memory
allocation failure.
Diffstat (limited to 'Modules/main.c')
-rw-r--r-- | Modules/main.c | 440 |
1 files changed, 322 insertions, 118 deletions
diff --git a/Modules/main.c b/Modules/main.c index 6391ba7..95076c6 100644 --- a/Modules/main.c +++ b/Modules/main.c @@ -111,7 +111,7 @@ static const char usage_6[] = " locale coercion and locale compatibility warnings on stderr.\n"; static void -usage(int error, const wchar_t* program) +pymain_usage(int error, const wchar_t* program) { FILE *f = error ? stderr : stdout; @@ -128,6 +128,20 @@ usage(int error, const wchar_t* program) } } + +static char* +pymain_get_env_var(const char *name) +{ + char *var = Py_GETENV(name); + if (var && var[0] != '\0') { + return var; + } + else { + return NULL; + } +} + + static void pymain_run_statup(PyCompilerFlags *cf) { @@ -444,7 +458,7 @@ pymain_free_impl(_PyMain *pymain) static void pymain_free(_PyMain *pymain) { - /* Call pymain_free() with the memory allocator used by pymain_init() */ + /* Force malloc() memory allocator */ PyMemAllocatorEx old_alloc, raw_alloc; PyMem_GetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); _PyMem_GetDefaultRawAllocator(&raw_alloc); @@ -488,11 +502,12 @@ error: static wchar_t* -pymain_strdup(wchar_t *str) +pymain_strdup(_PyMain *pymain, wchar_t *str) { size_t len = wcslen(str) + 1; /* +1 for NUL character */ wchar_t *str2 = PyMem_RawMalloc(sizeof(wchar_t) * len); if (str2 == NULL) { + pymain->err = INIT_NO_MEMORY(); return NULL; } memcpy(str2, str, len * sizeof(wchar_t)); @@ -501,9 +516,9 @@ pymain_strdup(wchar_t *str) static int -pymain_optlist_append(_Py_OptList *list, wchar_t *str) +pymain_optlist_append(_PyMain *pymain, _Py_OptList *list, wchar_t *str) { - wchar_t *str2 = pymain_strdup(str); + wchar_t *str2 = pymain_strdup(pymain, str); if (str2 == NULL) { return -1; } @@ -512,6 +527,7 @@ pymain_optlist_append(_Py_OptList *list, wchar_t *str) wchar_t **options2 = (wchar_t **)PyMem_RawRealloc(list->options, size); if (options2 == NULL) { PyMem_RawFree(str2); + pymain->err = INIT_NO_MEMORY(); return -1; } options2[list->len] = str2; @@ -526,7 +542,7 @@ pymain_optlist_append(_Py_OptList *list, wchar_t *str) Return 1 if parsing failed. Set pymain->err and return -1 on other errors. */ static int -pymain_parse_cmdline(_PyMain *pymain) +pymain_parse_cmdline_impl(_PyMain *pymain) { _Py_CommandLineDetails *cmdline = &pymain->cmdline; @@ -544,7 +560,8 @@ pymain_parse_cmdline(_PyMain *pymain) size_t len = wcslen(_PyOS_optarg) + 1 + 1; wchar_t *command = PyMem_RawMalloc(sizeof(wchar_t) * len); if (command == NULL) { - goto out_of_memory; + pymain->err = INIT_NO_MEMORY(); + return -1; } memcpy(command, _PyOS_optarg, len * sizeof(wchar_t)); command[len - 2] = '\n'; @@ -629,16 +646,16 @@ pymain_parse_cmdline(_PyMain *pymain) break; case 'W': - if (pymain_optlist_append(&cmdline->warning_options, + if (pymain_optlist_append(pymain, &cmdline->warning_options, _PyOS_optarg) < 0) { - goto out_of_memory; + return -1; } break; case 'X': - if (pymain_optlist_append(&cmdline->xoptions, + if (pymain_optlist_append(pymain, &cmdline->xoptions, _PyOS_optarg) < 0) { - goto out_of_memory; + return -1; } break; @@ -666,10 +683,6 @@ pymain_parse_cmdline(_PyMain *pymain) } return 0; - -out_of_memory: - pymain->err = INIT_NO_MEMORY(); - return -1; } @@ -753,17 +766,18 @@ pymain_warnings_envvar(_PyMain *pymain) buf = (wchar_t *)PyMem_RawMalloc((wcslen(wp) + 1) * sizeof(wchar_t)); if (buf == NULL) { - goto out_of_memory; + pymain->err = INIT_NO_MEMORY(); + return -1; } wcscpy(buf, wp); for (warning = wcstok_s(buf, L",", &context); warning != NULL; warning = wcstok_s(NULL, L",", &context)) { - if (pymain_optlist_append(&pymain->env_warning_options, + if (pymain_optlist_append(pymain, &pymain->env_warning_options, warning) < 0) { PyMem_RawFree(buf); - goto out_of_memory; + return -1; } } PyMem_RawFree(buf); @@ -778,7 +792,8 @@ pymain_warnings_envvar(_PyMain *pymain) C89 wcstok */ buf = (char *)PyMem_RawMalloc(strlen(p) + 1); if (buf == NULL) { - goto out_of_memory; + pymain->err = INIT_NO_MEMORY(); + return -1; } strcpy(buf, p); oldloc = _PyMem_RawStrdup(setlocale(LC_ALL, NULL)); @@ -793,13 +808,14 @@ pymain_warnings_envvar(_PyMain *pymain) return -1; } else { - goto out_of_memory; + pymain->err = INIT_NO_MEMORY(); + return -1; } } - if (pymain_optlist_append(&pymain->env_warning_options, + if (pymain_optlist_append(pymain, &pymain->env_warning_options, warning) < 0) { PyMem_RawFree(warning); - goto out_of_memory; + return -1; } PyMem_RawFree(warning); } @@ -809,10 +825,6 @@ pymain_warnings_envvar(_PyMain *pymain) } #endif return 0; - -out_of_memory: - pymain->err = INIT_NO_MEMORY(); - return -1; } @@ -882,7 +894,8 @@ pymain_get_program_name(_PyMain *pymain) buffer = PyMem_RawMalloc(len * sizeof(wchar_t)); if (buffer == NULL) { - goto out_of_memory; + pymain->err = INIT_NO_MEMORY(); + return -1; } mbstowcs(buffer, p, len); @@ -904,7 +917,8 @@ pymain_get_program_name(_PyMain *pymain) return -1; } else { - goto out_of_memory; + pymain->err = INIT_NO_MEMORY(); + return -1; } } pymain->program_name = wbuf; @@ -915,16 +929,12 @@ pymain_get_program_name(_PyMain *pymain) if (pymain->program_name == NULL) { /* Use argv[0] by default */ - pymain->program_name = pymain_strdup(pymain->argv[0]); + pymain->program_name = pymain_strdup(pymain, pymain->argv[0]); if (pymain->program_name == NULL) { - goto out_of_memory; + return -1; } } return 0; - -out_of_memory: - pymain->err = INIT_NO_MEMORY(); - return -1; } @@ -982,7 +992,7 @@ pymain_header(_PyMain *pymain) static void -pymain_init_argv(_PyMain *pymain) +pymain_set_argv(_PyMain *pymain) { _Py_CommandLineDetails *cmdline = &pymain->cmdline; @@ -999,10 +1009,6 @@ pymain_init_argv(_PyMain *pymain) pymain->argv[_PyOS_optind] = L"-m"; } - if (cmdline->filename != NULL) { - pymain->main_importer_path = pymain_get_importer(cmdline->filename); - } - int update_path; if (pymain->main_importer_path != NULL) { /* Let pymain_run_main_from_importer() adjust sys.path[0] later */ @@ -1039,28 +1045,6 @@ pymain_set_global_config(_PyMain *pymain) } -/* Propagate options parsed from the command line and environment variables - to the Python runtime. - - Return 0 on success, or set pymain->err and return -1 on error. */ -static int -pymain_configure_pyruntime(_PyMain *pymain) -{ - Py_SetProgramName(pymain->program_name); - /* Don't free program_name here: the argument to Py_SetProgramName - must remain valid until Py_FinalizeEx is called. The string is freed - by pymain_free(). */ - - if (pymain_add_xoptions(pymain)) { - return -1; - } - if (pymain_add_warnings_options(pymain)) { - return -1; - } - return 0; -} - - static void pymain_import_readline(_PyMain *pymain) { @@ -1135,7 +1119,7 @@ pymain_open_filename(_PyMain *pymain) static void -pymain_run(_PyMain *pymain) +pymain_run_filename(_PyMain *pymain) { _Py_CommandLineDetails *cmdline = &pymain->cmdline; @@ -1169,24 +1153,22 @@ pymain_run(_PyMain *pymain) static void pymain_repl(_PyMain *pymain) { - char *p; - /* Check this environment variable at the end, to give programs the - * opportunity to set it from Python. - */ - if (!Py_InspectFlag && - (p = Py_GETENV("PYTHONINSPECT")) && *p != '\0') - { + opportunity to set it from Python. */ + if (!Py_InspectFlag && pymain_get_env_var("PYTHONINSPECT")) { Py_InspectFlag = 1; } - if (Py_InspectFlag && pymain->stdin_is_interactive && pymain->run_code) { - Py_InspectFlag = 0; - pymain_run_interactive_hook(); - /* XXX */ - int res = PyRun_AnyFileFlags(stdin, "<stdin>", &pymain->cf); - pymain->status = (res != 0); + if (!(Py_InspectFlag && pymain->stdin_is_interactive + && pymain->run_code)) { + return; } + + Py_InspectFlag = 0; + pymain_run_interactive_hook(); + /* XXX */ + int res = PyRun_AnyFileFlags(stdin, "<stdin>", &pymain->cf); + pymain->status = (res != 0); } @@ -1197,22 +1179,22 @@ pymain_repl(_PyMain *pymain) Return 0 on success. Set pymain->err and return -1 on failure. */ static int -pymain_init_cmdline(_PyMain *pymain) +pymain_parse_cmdline(_PyMain *pymain) { _Py_CommandLineDetails *cmdline = &pymain->cmdline; - int res = pymain_parse_cmdline(pymain); + int res = pymain_parse_cmdline_impl(pymain); if (res < 0) { return -1; } if (res) { - usage(1, pymain->argv[0]); + pymain_usage(1, pymain->argv[0]); pymain->status = 2; return 1; } if (cmdline->print_help) { - usage(0, pymain->argv[0]); + pymain_usage(0, pymain->argv[0]); pymain->status = 0; return 1; } @@ -1230,34 +1212,170 @@ pymain_init_cmdline(_PyMain *pymain) } -/* Initialize Py_Main(). - This code must not use Python runtime apart PyMem_Raw memory allocator. +static wchar_t* +pymain_get_xoption(_PyMain *pymain, wchar_t *name) +{ + _Py_OptList *list = &pymain->cmdline.xoptions; + for (size_t i=0; i < list->len; i++) { + wchar_t *option = list->options[i]; + size_t len; + wchar_t *sep = wcschr(option, L'='); + if (sep != NULL) { + len = (sep - option); + } + else { + len = wcslen(option); + } + if (wcsncmp(option, name, len) == 0 && name[len] == L'\0') { + return option; + } + } + return NULL; +} + - Return 0 on success. - Return 1 if Python is done and must exit. - Set pymain->err and return -1 on error. */ static int -pymain_init_impl(_PyMain *pymain) +pymain_str_to_int(char *str, int *result) { - _PyCoreConfig *core_config = &pymain->core_config; - core_config->_disable_importlib = 0; + errno = 0; + char *endptr = str; + long value = strtol(str, &endptr, 10); + if (*endptr != '\0' || errno == ERANGE) { + return -1; + } + if (value < INT_MIN || value > INT_MAX) { + return -1; + } - orig_argc = pymain->argc; /* For Py_GetArgcArgv() */ - orig_argv = pymain->argv; + *result = (int)value; + return 0; +} - /* Parse the command line */ - int res = pymain_init_cmdline(pymain); - if (res < 0) { + +static int +pymain_wstr_to_int(wchar_t *wstr, int *result) +{ + errno = 0; + wchar_t *endptr = wstr; + long value = wcstol(wstr, &endptr, 10); + if (*endptr != '\0' || errno == ERANGE) { return -1; } - if (res > 0) { - return 1; + if (value < INT_MIN || value > INT_MAX) { + return -1; } - pymain_set_global_config(pymain); - pymain_init_stdio(pymain); + *result = (int)value; + return 0; +} + + +static int +pymain_init_tracemalloc(_PyMain *pymain) +{ + int nframe; + int valid; + + char *env = pymain_get_env_var("PYTHONTRACEMALLOC"); + if (env) { + if (!pymain_str_to_int(env, &nframe)) { + valid = (nframe >= 1); + } + else { + valid = 0; + } + if (!valid) { + pymain->err = _Py_INIT_USER_ERR("PYTHONTRACEMALLOC: invalid " + "number of frames"); + return -1; + } + pymain->core_config.tracemalloc = nframe; + } + + wchar_t *xoption = pymain_get_xoption(pymain, L"tracemalloc"); + if (xoption) { + wchar_t *sep = wcschr(xoption, L'='); + if (sep) { + if (!pymain_wstr_to_int(sep + 1, &nframe)) { + valid = (nframe >= 1); + } + else { + valid = 0; + } + if (!valid) { + pymain->err = _Py_INIT_USER_ERR("-X tracemalloc=NFRAME: " + "invalid number of frames"); + return -1; + } + } + else { + /* -X tracemalloc behaves as -X tracemalloc=1 */ + nframe = 1; + } + pymain->core_config.tracemalloc = nframe; + } + return 0; +} + + +static void +pymain_set_flag_from_env(int *flag, const char *name) +{ + char *var = pymain_get_env_var(name); + if (!var) { + return; + } + int value; + if (pymain_str_to_int(var, &value) < 0 || value < 0) { + /* PYTHONDEBUG=text and PYTHONDEBUG=-2 behave as PYTHONDEBUG=1 */ + value = 1; + } + if (*flag < value) { + *flag = value; + } +} + + +static void +pymain_set_flags_from_env(_PyMain *pymain) +{ + pymain_set_flag_from_env(&Py_DebugFlag, + "PYTHONDEBUG"); + pymain_set_flag_from_env(&Py_VerboseFlag, + "PYTHONVERBOSE"); + pymain_set_flag_from_env(&Py_OptimizeFlag, + "PYTHONOPTIMIZE"); + pymain_set_flag_from_env(&Py_InspectFlag, + "PYTHONINSPECT"); + pymain_set_flag_from_env(&Py_DontWriteBytecodeFlag, + "PYTHONDONTWRITEBYTECODE"); + pymain_set_flag_from_env(&Py_NoUserSiteDirectory, + "PYTHONNOUSERSITE"); + pymain_set_flag_from_env(&Py_UnbufferedStdioFlag, + "PYTHONUNBUFFERED"); +#ifdef MS_WINDOWS + pymain_set_flag_from_env(&Py_LegacyWindowsFSEncodingFlag, + "PYTHONLEGACYWINDOWSFSENCODING"); + pymain_set_flag_from_env(&Py_LegacyWindowsStdioFlag, + "PYTHONLEGACYWINDOWSSTDIO"); +#endif +} + + +static int +pymain_parse_envvars(_PyMain *pymain) +{ + _PyCoreConfig *core_config = &pymain->core_config; /* Get environment variables */ + pymain_set_flags_from_env(pymain); + + /* The variable is only tested for existence here; + _Py_HashRandomization_Init will check its value further. */ + if (pymain_get_env_var("PYTHONHASHSEED")) { + Py_HashRandomizationFlag = 1; + } + if (pymain_warnings_envvar(pymain) < 0) { return -1; } @@ -1266,26 +1384,59 @@ pymain_init_impl(_PyMain *pymain) } core_config->allocator = Py_GETENV("PYTHONMALLOC"); + /* More complex options: env var and/or -X option */ + if (pymain_get_env_var("PYTHONFAULTHANDLER") + || pymain_get_xoption(pymain, L"faulthandler")) { + core_config->faulthandler = 1; + } + if (pymain_get_env_var("PYTHONPROFILEIMPORTTIME") + || pymain_get_xoption(pymain, L"importtime")) { + core_config->importtime = 1; + } + if (pymain_init_tracemalloc(pymain) < 0) { + return -1; + } return 0; } +/* Parse command line options and environment variables. + This code must not use Python runtime apart PyMem_Raw memory allocator. + + Return 0 on success. + Return 1 if Python is done and must exit. + Set pymain->err and return -1 on error. */ static int -pymain_init(_PyMain *pymain) +pymain_parse_cmdline_envvars_impl(_PyMain *pymain) { - pymain->err = _PyRuntime_Initialize(); - if (_Py_INIT_FAILED(pymain->err)) { + int res = pymain_parse_cmdline(pymain); + if (res < 0) { + return -1; + } + if (res > 0) { + return 1; + } + + pymain_set_global_config(pymain); + + if (pymain_parse_envvars(pymain) < 0) { return -1; } - /* Make sure that all memory allocated in pymain_init() is allocated - by malloc() */ + return 0; +} + + +static int +pymain_parse_cmdline_envvars(_PyMain *pymain) +{ + /* Force malloc() memory allocator */ PyMemAllocatorEx old_alloc, raw_alloc; PyMem_GetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); _PyMem_GetDefaultRawAllocator(&raw_alloc); PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &raw_alloc); - int res = pymain_init_impl(pymain); + int res = pymain_parse_cmdline_envvars_impl(pymain); /* Restore the old memory allocator */ PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); @@ -1294,27 +1445,47 @@ pymain_init(_PyMain *pymain) } static int -pymain_core(_PyMain *pymain) +pymain_init_python(_PyMain *pymain) { - _Py_CommandLineDetails *cmdline = &pymain->cmdline; + pymain_init_stdio(pymain); pymain->err = _Py_InitializeCore(&pymain->core_config); if (_Py_INIT_FAILED(pymain->err)) { return -1; } - if (pymain_configure_pyruntime(pymain)) { + Py_SetProgramName(pymain->program_name); + /* Don't free program_name here: the argument to Py_SetProgramName + must remain valid until Py_FinalizeEx is called. The string is freed + by pymain_free(). */ + + if (pymain_add_xoptions(pymain)) { + return -1; + } + if (pymain_add_warnings_options(pymain)) { return -1; } if (pymain_init_main_interpreter(pymain)) { return -1; } + return 0; +} + + +static void +pymain_run_python(_PyMain *pymain) +{ + _Py_CommandLineDetails *cmdline = &pymain->cmdline; pymain_header(pymain); pymain_import_readline(pymain); - pymain_init_argv(pymain); + if (cmdline->filename != NULL) { + pymain->main_importer_path = pymain_get_importer(cmdline->filename); + } + + pymain_set_argv(pymain); if (cmdline->command) { pymain->status = pymain_run_command(cmdline->command, &pymain->cf); @@ -1323,16 +1494,57 @@ pymain_core(_PyMain *pymain) pymain->status = (pymain_run_module(cmdline->module, 1) != 0); } else { - pymain_run(pymain); + pymain_run_filename(pymain); } pymain_repl(pymain); +} + + +static int +pymain_init(_PyMain *pymain) +{ + pymain->err = _PyRuntime_Initialize(); + if (_Py_INIT_FAILED(pymain->err)) { + return -1; + } + + pymain->core_config._disable_importlib = 0; + + orig_argc = pymain->argc; /* For Py_GetArgcArgv() */ + orig_argv = pymain->argv; + return 0; +} + + +static int +pymain_impl(_PyMain *pymain) +{ + int res = pymain_init(pymain); + if (res < 0) { + return -1; + } + + res = pymain_parse_cmdline_envvars(pymain); + if (res < 0) { + return -1; + } + if (res > 0) { + /* --help or --version command: we are done */ + return 0; + } + + res = pymain_init_python(pymain); + if (res < 0) { + return -1; + } + + pymain_run_python(pymain); if (Py_FinalizeEx() < 0) { /* Value unlikely to be confused with a non-error exit status or - other special meaning */ + other special meaning */ pymain->status = 120; } - return 0; } @@ -1345,17 +1557,9 @@ Py_Main(int argc, wchar_t **argv) pymain.argc = argc; pymain.argv = argv; - int res = pymain_init(&pymain); - if (res < 0) { + if (pymain_impl(&pymain) < 0) { _Py_FatalInitError(pymain.err); } - if (res == 0) { - res = pymain_core(&pymain); - if (res < 0) { - _Py_FatalInitError(pymain.err); - } - } - pymain_free(&pymain); return pymain.status; |