From 95e2cbf32f8156c239b27dae558ba058d0f2d496 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 1 Mar 2019 16:25:19 +0100 Subject: [PATCH] bpo-36142: Move command line parsing to coreconfig.c (GH-12123) * Add _PyCoreConfig_ReadFromArgv() function which parses command line options: move code from main.c to coreconfig.c. * Add _PyCoreConfig_Write() to write the new configuration: coerce the LC_CTYPE locale, set Py_xxx global configuration variables, etc. * _PyCoreConfig_ReadFromArgv() now only changes the LC_CTYPE locale temporarily. _PyCoreConfig_Write() becomes responsible to set the LC_CTYPE locale. * Add _Py_SetArgcArgv() and _Py_ClearArgcArgv() functions * Rename many "pymain_xxx()" functions * Add "const" to some function parameters * Reorganize main.c to declare functions in the order in which they are called. --- Include/cpython/coreconfig.h | 10 + Include/internal/pycore_coreconfig.h | 26 + Include/internal/pycore_pathconfig.h | 14 - Modules/main.c | 1963 +++++++------------------- Python/coreconfig.c | 975 ++++++++++++- 5 files changed, 1523 insertions(+), 1465 deletions(-) diff --git a/Include/cpython/coreconfig.h b/Include/cpython/coreconfig.h index 4985bf3a759..8109d4abc50 100644 --- a/Include/cpython/coreconfig.h +++ b/Include/cpython/coreconfig.h @@ -5,6 +5,16 @@ extern "C" { #endif +/* _PyArgv */ + +typedef struct { + int argc; + int use_bytes_argv; + char **bytes_argv; + wchar_t **wchar_argv; +} _PyArgv; + + /* _PyInitError */ typedef struct { diff --git a/Include/internal/pycore_coreconfig.h b/Include/internal/pycore_coreconfig.h index b906ac4f7f9..ebb0a9e9f1f 100644 --- a/Include/internal/pycore_coreconfig.h +++ b/Include/internal/pycore_coreconfig.h @@ -8,6 +8,29 @@ extern "C" { # error "this header requires Py_BUILD_CORE or Py_BUILD_CORE_BUILTIN defined" #endif +/* _Py_wstrlist */ + +PyAPI_FUNC(void) _Py_wstrlist_clear( + int len, + wchar_t **list); +PyAPI_FUNC(wchar_t**) _Py_wstrlist_copy( + int len, + wchar_t * const *list); +PyAPI_FUNC(_PyInitError) _Py_wstrlist_append( + int *len, + wchar_t ***list, + const wchar_t *str); +PyAPI_FUNC(PyObject*) _Py_wstrlist_as_pylist( + int len, + wchar_t **list); + +/* Py_GetArgcArgv() helpers */ + +PyAPI_FUNC(void) _Py_ClearArgcArgv(void); +PyAPI_FUNC(int) _Py_SetArgcArgv(int argc, wchar_t * const *argv); + +/* _PyCoreConfig */ + PyAPI_FUNC(_PyInitError) _PyCoreConfig_Read(_PyCoreConfig *config); PyAPI_FUNC(void) _PyCoreConfig_Clear(_PyCoreConfig *); PyAPI_FUNC(int) _PyCoreConfig_Copy( @@ -26,6 +49,9 @@ PyAPI_FUNC(int) _PyCoreConfig_GetEnvDup( wchar_t **dest, wchar_t *wname, char *name); +PyAPI_FUNC(_PyInitError) _PyCoreConfig_ReadFromArgv(_PyCoreConfig *config, + const _PyArgv *args); +PyAPI_FUNC(void) _PyCoreConfig_Write(const _PyCoreConfig *config); #ifdef __cplusplus } diff --git a/Include/internal/pycore_pathconfig.h b/Include/internal/pycore_pathconfig.h index c0731525ca1..fb6b1d78b36 100644 --- a/Include/internal/pycore_pathconfig.h +++ b/Include/internal/pycore_pathconfig.h @@ -8,20 +8,6 @@ extern "C" { # error "this header requires Py_BUILD_CORE or Py_BUILD_CORE_BUILTIN define" #endif -PyAPI_FUNC(void) _Py_wstrlist_clear( - int len, - wchar_t **list); -PyAPI_FUNC(wchar_t**) _Py_wstrlist_copy( - int len, - wchar_t **list); -PyAPI_FUNC(_PyInitError) _Py_wstrlist_append( - int *len, - wchar_t ***list, - const wchar_t *str); -PyAPI_FUNC(PyObject*) _Py_wstrlist_as_pylist( - int len, - wchar_t **list); - typedef struct _PyPathConfig { /* Full path to the Python program */ wchar_t *program_full_path; diff --git a/Modules/main.c b/Modules/main.c index e7d75a79e78..fdeaf1d12ad 100644 --- a/Modules/main.c +++ b/Modules/main.c @@ -1,46 +1,27 @@ /* Python interpreter main program */ #include "Python.h" -#include "osdefs.h" #include "pycore_coreconfig.h" -#include "pycore_getopt.h" -#include "pycore_pathconfig.h" #include "pycore_pylifecycle.h" #include "pycore_pymem.h" #include "pycore_pystate.h" -#include -#ifdef HAVE_SIGNAL_H -#include -#endif -#include -#if defined(HAVE_GETPID) && defined(HAVE_UNISTD_H) -#include -#endif - -#if defined(MS_WINDOWS) || defined(__CYGWIN__) -# include -# ifdef HAVE_IO_H -# include -# endif -# ifdef HAVE_FCNTL_H -# include -# endif -#endif - -#ifdef _MSC_VER -# include -#endif - #ifdef __FreeBSD__ -# include +# include /* fedisableexcept() */ #endif -#if defined(MS_WINDOWS) -# define PYTHONHOMEHELP "\\python{major}{minor}" -#else -# define PYTHONHOMEHELP "/lib/pythonX.X" +/* Includes for exit_sigint() */ +#include /* perror() */ +#ifdef HAVE_SIGNAL_H +# include /* SIGINT */ #endif +#if defined(HAVE_GETPID) && defined(HAVE_UNISTD_H) +# include /* getpid() */ +#endif +#ifdef _MSC_VER +# include /* STATUS_CONTROL_C_EXIT */ +#endif +/* End of includes for exit_sigint() */ #define COPYRIGHT \ "Type \"help\", \"copyright\", \"credits\" or \"license\" " \ @@ -50,656 +31,26 @@ extern "C" { #endif -#define DECODE_LOCALE_ERR(NAME, LEN) \ - (((LEN) == -2) \ - ? _Py_INIT_USER_ERR("cannot decode " NAME) \ - : _Py_INIT_NO_MEMORY()) +/* --- PyMainInterpreter ------------------------------------------ */ - -#ifdef MS_WINDOWS -#define WCSTOK wcstok_s -#else -#define WCSTOK wcstok -#endif - -/* For Py_GetArgcArgv(); set by main() */ -static wchar_t **orig_argv = NULL; -static int orig_argc = 0; - -/* command line options */ -#define BASE_OPTS L"bBc:dEhiIJm:OqRsStuvVW:xX:?" - -#define PROGRAM_OPTS BASE_OPTS - -static const _PyOS_LongOption longoptions[] = { - {L"check-hash-based-pycs", 1, 0}, - {NULL, 0, 0}, -}; - -/* Short usage message (with %s for argv0) */ -static const char usage_line[] = -"usage: %ls [option] ... [-c cmd | -m mod | file | -] [arg] ...\n"; - -/* Long usage message, split into parts < 512 bytes */ -static const char usage_1[] = "\ -Options and arguments (and corresponding environment variables):\n\ --b : issue warnings about str(bytes_instance), str(bytearray_instance)\n\ - and comparing bytes/bytearray with str. (-bb: issue errors)\n\ --B : don't write .pyc files on import; also PYTHONDONTWRITEBYTECODE=x\n\ --c cmd : program passed in as string (terminates option list)\n\ --d : debug output from parser; also PYTHONDEBUG=x\n\ --E : ignore PYTHON* environment variables (such as PYTHONPATH)\n\ --h : print this help message and exit (also --help)\n\ -"; -static const char usage_2[] = "\ --i : inspect interactively after running script; forces a prompt even\n\ - if stdin does not appear to be a terminal; also PYTHONINSPECT=x\n\ --I : isolate Python from the user's environment (implies -E and -s)\n\ --m mod : run library module as a script (terminates option list)\n\ --O : remove assert and __debug__-dependent statements; add .opt-1 before\n\ - .pyc extension; also PYTHONOPTIMIZE=x\n\ --OO : do -O changes and also discard docstrings; add .opt-2 before\n\ - .pyc extension\n\ --q : don't print version and copyright messages on interactive startup\n\ --s : don't add user site directory to sys.path; also PYTHONNOUSERSITE\n\ --S : don't imply 'import site' on initialization\n\ -"; -static const char usage_3[] = "\ --u : force the stdout and stderr streams to be unbuffered;\n\ - this option has no effect on stdin; also PYTHONUNBUFFERED=x\n\ --v : verbose (trace import statements); also PYTHONVERBOSE=x\n\ - can be supplied multiple times to increase verbosity\n\ --V : print the Python version number and exit (also --version)\n\ - when given twice, print more information about the build\n\ --W arg : warning control; arg is action:message:category:module:lineno\n\ - also PYTHONWARNINGS=arg\n\ --x : skip first line of source, allowing use of non-Unix forms of #!cmd\n\ --X opt : set implementation-specific option\n\ ---check-hash-based-pycs always|default|never:\n\ - control how Python invalidates hash-based .pyc files\n\ -"; -static const char usage_4[] = "\ -file : program read from script file\n\ -- : program read from stdin (default; interactive mode if a tty)\n\ -arg ...: arguments passed to program in sys.argv[1:]\n\n\ -Other environment variables:\n\ -PYTHONSTARTUP: file executed on interactive startup (no default)\n\ -PYTHONPATH : '%lc'-separated list of directories prefixed to the\n\ - default module search path. The result is sys.path.\n\ -"; -static const char usage_5[] = -"PYTHONHOME : alternate directory (or %lc).\n" -" The default module search path uses %s.\n" -"PYTHONCASEOK : ignore case in 'import' statements (Windows).\n" -"PYTHONIOENCODING: Encoding[:errors] used for stdin/stdout/stderr.\n" -"PYTHONFAULTHANDLER: dump the Python traceback on fatal errors.\n"; -static const char usage_6[] = -"PYTHONHASHSEED: if this variable is set to 'random', a random value is used\n" -" to seed the hashes of str, bytes and datetime objects. It can also be\n" -" set to an integer in the range [0,4294967295] to get hash values with a\n" -" predictable seed.\n" -"PYTHONMALLOC: set the Python memory allocators and/or install debug hooks\n" -" on Python memory allocators. Use PYTHONMALLOC=debug to install debug\n" -" hooks.\n" -"PYTHONCOERCECLOCALE: if this variable is set to 0, it disables the locale\n" -" coercion behavior. Use PYTHONCOERCECLOCALE=warn to request display of\n" -" locale coercion and locale compatibility warnings on stderr.\n" -"PYTHONBREAKPOINT: if this variable is set to 0, it disables the default\n" -" debugger. It can be set to the callable of your debugger of choice.\n" -"PYTHONDEVMODE: enable the development mode.\n" -"PYTHONPYCACHEPREFIX: root directory for bytecode cache (pyc) files.\n"; - -static void -pymain_usage(int error, const wchar_t* program) +void +_PyMainInterpreterConfig_Clear(_PyMainInterpreterConfig *config) { - FILE *f = error ? stderr : stdout; - - fprintf(f, usage_line, program); - if (error) - fprintf(f, "Try `python -h' for more information.\n"); - else { - fputs(usage_1, f); - fputs(usage_2, f); - fputs(usage_3, f); - fprintf(f, usage_4, (wint_t)DELIM); - fprintf(f, usage_5, (wint_t)DELIM, PYTHONHOMEHELP); - fputs(usage_6, f); - } -} - - -static void -pymain_run_interactive_hook(void) -{ - PyObject *sys, *hook, *result; - sys = PyImport_ImportModule("sys"); - if (sys == NULL) { - goto error; - } - - hook = PyObject_GetAttrString(sys, "__interactivehook__"); - Py_DECREF(sys); - if (hook == NULL) { - PyErr_Clear(); - return; - } - - result = _PyObject_CallNoArg(hook); - Py_DECREF(hook); - if (result == NULL) { - goto error; - } - Py_DECREF(result); - - return; - -error: - PySys_WriteStderr("Failed calling sys.__interactivehook__\n"); - PyErr_Print(); + Py_CLEAR(config->argv); + Py_CLEAR(config->executable); + Py_CLEAR(config->prefix); + Py_CLEAR(config->base_prefix); + Py_CLEAR(config->exec_prefix); + Py_CLEAR(config->base_exec_prefix); + Py_CLEAR(config->warnoptions); + Py_CLEAR(config->xoptions); + Py_CLEAR(config->module_search_path); + Py_CLEAR(config->pycache_prefix); } static int -pymain_run_module(const wchar_t *modname, int set_argv0) -{ - PyObject *module, *runpy, *runmodule, *runargs, *result; - runpy = PyImport_ImportModule("runpy"); - if (runpy == NULL) { - fprintf(stderr, "Could not import runpy module\n"); - PyErr_Print(); - return -1; - } - runmodule = PyObject_GetAttrString(runpy, "_run_module_as_main"); - if (runmodule == NULL) { - fprintf(stderr, "Could not access runpy._run_module_as_main\n"); - PyErr_Print(); - Py_DECREF(runpy); - return -1; - } - module = PyUnicode_FromWideChar(modname, wcslen(modname)); - if (module == NULL) { - fprintf(stderr, "Could not convert module name to unicode\n"); - PyErr_Print(); - Py_DECREF(runpy); - Py_DECREF(runmodule); - return -1; - } - runargs = Py_BuildValue("(Oi)", module, set_argv0); - if (runargs == NULL) { - fprintf(stderr, - "Could not create arguments for runpy._run_module_as_main\n"); - PyErr_Print(); - Py_DECREF(runpy); - Py_DECREF(runmodule); - Py_DECREF(module); - return -1; - } - result = PyObject_Call(runmodule, runargs, NULL); - if (result == NULL) { - PyErr_Print(); - } - Py_DECREF(runpy); - Py_DECREF(runmodule); - Py_DECREF(module); - Py_DECREF(runargs); - if (result == NULL) { - return -1; - } - Py_DECREF(result); - return 0; -} - -static PyObject * -pymain_get_importer(const wchar_t *filename) -{ - PyObject *sys_path0 = NULL, *importer; - - sys_path0 = PyUnicode_FromWideChar(filename, wcslen(filename)); - if (sys_path0 == NULL) { - goto error; - } - - importer = PyImport_GetImporter(sys_path0); - if (importer == NULL) { - goto error; - } - - if (importer == Py_None) { - Py_DECREF(sys_path0); - Py_DECREF(importer); - return NULL; - } - - Py_DECREF(importer); - return sys_path0; - -error: - Py_XDECREF(sys_path0); - PySys_WriteStderr("Failed checking if argv[0] is an import path entry\n"); - PyErr_Print(); - return NULL; -} - - -static int -pymain_run_command(wchar_t *command, PyCompilerFlags *cf) -{ - PyObject *unicode, *bytes; - int ret; - - unicode = PyUnicode_FromWideChar(command, -1); - if (unicode == NULL) { - goto error; - } - - bytes = PyUnicode_AsUTF8String(unicode); - Py_DECREF(unicode); - if (bytes == NULL) { - goto error; - } - - ret = PyRun_SimpleStringFlags(PyBytes_AsString(bytes), cf); - Py_DECREF(bytes); - return (ret != 0); - -error: - PySys_WriteStderr("Unable to decode the command from the command line:\n"); - PyErr_Print(); - return 1; -} - - -/* Main program */ - -typedef struct { - int argc; - int use_bytes_argv; - char **bytes_argv; - wchar_t **wchar_argv; -} _PyArgv; - -typedef struct { - const _PyArgv *args; - wchar_t **argv; - int nwarnoption; /* Number of -W command line options */ - wchar_t **warnoptions; /* Command line -W options */ - int nenv_warnoption; /* Number of PYTHONWARNINGS environment variables */ - wchar_t **env_warnoptions; /* PYTHONWARNINGS environment variables */ - int print_help; /* -h, -? options */ - int print_version; /* -V option */ -} _PyCmdline; - - - -/* Non-zero if filename, command (-c) or module (-m) is set - on the command line */ -#define RUN_CODE(config) \ - (config->run_command != NULL || config->run_filename != NULL \ - || config->run_module != NULL) - - -static _PyInitError -pymain_init_cmdline_argv(_PyCoreConfig *config, _PyCmdline *cmdline) -{ - assert(cmdline->argv == NULL); - - const _PyArgv *args = cmdline->args; - - if (args->use_bytes_argv) { - /* +1 for a the NULL terminator */ - size_t size = sizeof(wchar_t*) * (args->argc + 1); - wchar_t** argv = (wchar_t **)PyMem_RawMalloc(size); - if (argv == NULL) { - return _Py_INIT_NO_MEMORY(); - } - - for (int i = 0; i < args->argc; i++) { - size_t len; - wchar_t *arg = Py_DecodeLocale(args->bytes_argv[i], &len); - if (arg == NULL) { - _Py_wstrlist_clear(i, argv); - return DECODE_LOCALE_ERR("command line arguments", - (Py_ssize_t)len); - } - argv[i] = arg; - } - argv[args->argc] = NULL; - - cmdline->argv = argv; - } - else { - cmdline->argv = args->wchar_argv; - } - - wchar_t *program; - if (args->argc >= 1 && cmdline->argv != NULL) { - program = cmdline->argv[0]; - } - else { - program = L""; - } - config->program = _PyMem_RawWcsdup(program); - if (config->program == NULL) { - return _Py_INIT_NO_MEMORY(); - } - - return _Py_INIT_OK(); -} - - -static void -pymain_clear_cmdline(_PyCmdline *cmdline) -{ - PyMemAllocatorEx old_alloc; - _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); - - _Py_wstrlist_clear(cmdline->nwarnoption, cmdline->warnoptions); - cmdline->nwarnoption = 0; - cmdline->warnoptions = NULL; - - _Py_wstrlist_clear(cmdline->nenv_warnoption, cmdline->env_warnoptions); - cmdline->nenv_warnoption = 0; - cmdline->env_warnoptions = NULL; - - if (cmdline->args->use_bytes_argv && cmdline->argv != NULL) { - _Py_wstrlist_clear(cmdline->args->argc, cmdline->argv); - } - cmdline->argv = NULL; - - PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); -} - - -static void -pymain_clear_config(_PyCoreConfig *config) -{ - /* Clear core config with the memory allocator - used by pymain_read_conf() */ - PyMemAllocatorEx old_alloc; - _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); - - _PyCoreConfig_Clear(config); - - PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); -} - - -static void -pymain_free(void) -{ - _PyImport_Fini2(); - - /* Free global variables which cannot be freed in Py_Finalize(): - configuration options set before Py_Initialize() which should - remain valid after Py_Finalize(), since - Py_Initialize()-Py_Finalize() can be called multiple times. */ - _PyPathConfig_ClearGlobal(); - _Py_ClearStandardStreamEncoding(); - - /* Force the allocator used by pymain_read_conf() */ - PyMemAllocatorEx old_alloc; - _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); - - _Py_wstrlist_clear(orig_argc, orig_argv); - orig_argc = 0; - orig_argv = NULL; - - PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); - -#ifdef __INSURE__ - /* Insure++ is a memory analysis tool that aids in discovering - * memory leaks and other memory problems. On Python exit, the - * interned string dictionaries are flagged as being in use at exit - * (which it is). Under normal circumstances, this is fine because - * the memory will be automatically reclaimed by the system. Under - * memory debugging, it's a huge source of useless noise, so we - * trade off slower shutdown for less distraction in the memory - * reports. -baw - */ - _Py_ReleaseInternedUnicodeStrings(); -#endif /* __INSURE__ */ -} - - -static int -pymain_sys_path_add_path0(PyInterpreterState *interp, PyObject *path0) -{ - _Py_IDENTIFIER(path); - PyObject *sys_path; - PyObject *sysdict = interp->sysdict; - if (sysdict != NULL) { - sys_path = _PyDict_GetItemIdWithError(sysdict, &PyId_path); - if (sys_path == NULL && PyErr_Occurred()) { - goto error; - } - } - else { - sys_path = NULL; - } - if (sys_path == NULL) { - PyErr_SetString(PyExc_RuntimeError, "unable to get sys.path"); - goto error; - } - - if (PyList_Insert(sys_path, 0, path0)) { - goto error; - } - return 0; - -error: - PyErr_Print(); - return -1; -} - - -_PyInitError -_Py_wstrlist_append(int *len, wchar_t ***list, const wchar_t *str) -{ - if (*len == INT_MAX) { - /* len+1 would overflow */ - return _Py_INIT_NO_MEMORY(); - } - wchar_t *str2 = _PyMem_RawWcsdup(str); - if (str2 == NULL) { - return _Py_INIT_NO_MEMORY(); - } - - size_t size = (*len + 1) * sizeof(list[0]); - wchar_t **list2 = (wchar_t **)PyMem_RawRealloc(*list, size); - if (list2 == NULL) { - PyMem_RawFree(str2); - return _Py_INIT_NO_MEMORY(); - } - list2[*len] = str2; - *list = list2; - (*len)++; - return _Py_INIT_OK(); -} - - -/* Parse the command line arguments */ -static _PyInitError -pymain_parse_cmdline_impl(_PyCoreConfig *config, _PyCmdline *cmdline, - int *need_usage) -{ - _PyInitError err; - _PyOS_ResetGetOpt(); - do { - int longindex = -1; - int c = _PyOS_GetOpt(cmdline->args->argc, cmdline->argv, PROGRAM_OPTS, - longoptions, &longindex); - if (c == EOF) { - break; - } - - if (c == 'c') { - /* -c is the last option; following arguments - that look like options are left for the - command to interpret. */ - size_t len = wcslen(_PyOS_optarg) + 1 + 1; - wchar_t *command = PyMem_RawMalloc(sizeof(wchar_t) * len); - if (command == NULL) { - return _Py_INIT_NO_MEMORY(); - } - memcpy(command, _PyOS_optarg, (len - 2) * sizeof(wchar_t)); - command[len - 2] = '\n'; - command[len - 1] = 0; - config->run_command = command; - break; - } - - if (c == 'm') { - /* -m is the last option; following arguments - that look like options are left for the - module to interpret. */ - config->run_module = _PyMem_RawWcsdup(_PyOS_optarg); - if (config->run_module == NULL) { - return _Py_INIT_NO_MEMORY(); - } - break; - } - - switch (c) { - case 0: - // Handle long option. - assert(longindex == 0); // Only one long option now. - if (!wcscmp(_PyOS_optarg, L"always")) { - config->_check_hash_pycs_mode = "always"; - } else if (!wcscmp(_PyOS_optarg, L"never")) { - config->_check_hash_pycs_mode = "never"; - } else if (!wcscmp(_PyOS_optarg, L"default")) { - config->_check_hash_pycs_mode = "default"; - } else { - fprintf(stderr, "--check-hash-based-pycs must be one of " - "'default', 'always', or 'never'\n"); - *need_usage = 1; - return _Py_INIT_OK(); - } - break; - - case 'b': - config->bytes_warning++; - break; - - case 'd': - config->parser_debug++; - break; - - case 'i': - config->inspect++; - config->interactive++; - break; - - case 'I': - config->isolated++; - break; - - /* case 'J': reserved for Jython */ - - case 'O': - config->optimization_level++; - break; - - case 'B': - config->write_bytecode = 0; - break; - - case 's': - config->user_site_directory = 0; - break; - - case 'S': - config->site_import = 0; - break; - - case 'E': - config->use_environment = 0; - break; - - case 't': - /* ignored for backwards compatibility */ - break; - - case 'u': - config->buffered_stdio = 0; - break; - - case 'v': - config->verbose++; - break; - - case 'x': - config->skip_source_first_line = 1; - break; - - case 'h': - case '?': - cmdline->print_help++; - break; - - case 'V': - cmdline->print_version++; - break; - - case 'W': - err = _Py_wstrlist_append(&cmdline->nwarnoption, - &cmdline->warnoptions, - _PyOS_optarg); - if (_Py_INIT_FAILED(err)) { - return err; - } - break; - - case 'X': - err = _Py_wstrlist_append(&config->nxoption, - &config->xoptions, - _PyOS_optarg); - if (_Py_INIT_FAILED(err)) { - return err; - } - break; - - case 'q': - config->quiet++; - break; - - case 'R': - config->use_hash_seed = 0; - break; - - /* This space reserved for other options */ - - default: - /* unknown argument: parsing failed */ - *need_usage = 1; - return _Py_INIT_OK(); - } - } while (1); - - if (config->run_command == NULL && config->run_module == NULL - && _PyOS_optind < cmdline->args->argc - && wcscmp(cmdline->argv[_PyOS_optind], L"-") != 0) - { - config->run_filename = _PyMem_RawWcsdup(cmdline->argv[_PyOS_optind]); - if (config->run_filename == NULL) { - return _Py_INIT_NO_MEMORY(); - } - } - - if (config->run_command != NULL || config->run_module != NULL) { - /* Backup _PyOS_optind */ - _PyOS_optind--; - } - - /* -c and -m options are exclusive */ - assert(!(config->run_command != NULL && config->run_module != NULL)); - - return _Py_INIT_OK(); -} - - -static int -add_xoption(PyObject *opts, const wchar_t *s) +mainconfig_add_xoption(PyObject *opts, const wchar_t *s) { PyObject *name, *value; @@ -731,7 +82,7 @@ error: static PyObject* -config_create_xoptions_dict(const _PyCoreConfig *config) +mainconfig_create_xoptions_dict(const _PyCoreConfig *config) { int nxoption = config->nxoption; wchar_t **xoptions = config->xoptions; @@ -742,7 +93,7 @@ config_create_xoptions_dict(const _PyCoreConfig *config) for (int i=0; i < nxoption; i++) { wchar_t *option = xoptions[i]; - if (add_xoption(dict, option) < 0) { + if (mainconfig_add_xoption(dict, option) < 0) { Py_DECREF(dict); return NULL; } @@ -752,607 +103,8 @@ config_create_xoptions_dict(const _PyCoreConfig *config) } -static _PyInitError -config_add_warnings_optlist(_PyCoreConfig *config, int len, wchar_t **options) -{ - for (int i = 0; i < len; i++) { - _PyInitError err = _Py_wstrlist_append(&config->nwarnoption, - &config->warnoptions, - options[i]); - if (_Py_INIT_FAILED(err)) { - return err; - } - } - return _Py_INIT_OK(); -} - - -static _PyInitError -config_init_warnoptions(_PyCoreConfig *config, _PyCmdline *cmdline) -{ - _PyInitError err; - - assert(config->nwarnoption == 0); - - /* The priority order for warnings configuration is (highest precedence - * first): - * - * - the BytesWarning filter, if needed ('-b', '-bb') - * - any '-W' command line options; then - * - the 'PYTHONWARNINGS' environment variable; then - * - the dev mode filter ('-X dev', 'PYTHONDEVMODE'); then - * - any implicit filters added by _warnings.c/warnings.py - * - * All settings except the last are passed to the warnings module via - * the `sys.warnoptions` list. Since the warnings module works on the basis - * of "the most recently added filter will be checked first", we add - * the lowest precedence entries first so that later entries override them. - */ - - if (config->dev_mode) { - err = _Py_wstrlist_append(&config->nwarnoption, - &config->warnoptions, - L"default"); - if (_Py_INIT_FAILED(err)) { - return err; - } - } - - err = config_add_warnings_optlist(config, - cmdline->nenv_warnoption, - cmdline->env_warnoptions); - if (_Py_INIT_FAILED(err)) { - return err; - } - - err = config_add_warnings_optlist(config, - cmdline->nwarnoption, - cmdline->warnoptions); - if (_Py_INIT_FAILED(err)) { - return err; - } - - /* If the bytes_warning_flag isn't set, bytesobject.c and bytearrayobject.c - * don't even try to emit a warning, so we skip setting the filter in that - * case. - */ - if (config->bytes_warning) { - wchar_t *filter; - if (config->bytes_warning> 1) { - filter = L"error::BytesWarning"; - } - else { - filter = L"default::BytesWarning"; - } - err = _Py_wstrlist_append(&config->nwarnoption, - &config->warnoptions, - filter); - if (_Py_INIT_FAILED(err)) { - return err; - } - } - return _Py_INIT_OK(); -} - - -/* Get warning options from PYTHONWARNINGS environment variable. */ -static _PyInitError -cmdline_init_env_warnoptions(const _PyCoreConfig *config, _PyCmdline *cmdline) -{ - wchar_t *env; - int res = _PyCoreConfig_GetEnvDup(config, &env, - L"PYTHONWARNINGS", "PYTHONWARNINGS"); - if (res < 0) { - return DECODE_LOCALE_ERR("PYTHONWARNINGS", res); - } - - if (env == NULL) { - return _Py_INIT_OK(); - } - - - wchar_t *warning, *context = NULL; - for (warning = WCSTOK(env, L",", &context); - warning != NULL; - warning = WCSTOK(NULL, L",", &context)) - { - _PyInitError err = _Py_wstrlist_append(&cmdline->nenv_warnoption, - &cmdline->env_warnoptions, - warning); - if (_Py_INIT_FAILED(err)) { - PyMem_RawFree(env); - return err; - } - } - PyMem_RawFree(env); - return _Py_INIT_OK(); -} - - -/* Return non-zero is stdin is a TTY or if -i command line option is used */ -static int -stdin_is_interactive(const _PyCoreConfig *config) -{ - return (isatty(fileno(stdin)) || config->interactive); -} - - -static void -pymain_init_stdio(_PyCoreConfig *config) -{ -#if defined(MS_WINDOWS) || defined(__CYGWIN__) - /* don't translate newlines (\r\n <=> \n) */ - _setmode(fileno(stdin), O_BINARY); - _setmode(fileno(stdout), O_BINARY); - _setmode(fileno(stderr), O_BINARY); -#endif - - if (!config->buffered_stdio) { -#ifdef HAVE_SETVBUF - setvbuf(stdin, (char *)NULL, _IONBF, BUFSIZ); - setvbuf(stdout, (char *)NULL, _IONBF, BUFSIZ); - setvbuf(stderr, (char *)NULL, _IONBF, BUFSIZ); -#else /* !HAVE_SETVBUF */ - setbuf(stdin, (char *)NULL); - setbuf(stdout, (char *)NULL); - setbuf(stderr, (char *)NULL); -#endif /* !HAVE_SETVBUF */ - } - else if (config->interactive) { -#ifdef MS_WINDOWS - /* Doesn't have to have line-buffered -- use unbuffered */ - /* Any set[v]buf(stdin, ...) screws up Tkinter :-( */ - setvbuf(stdout, (char *)NULL, _IONBF, BUFSIZ); -#else /* !MS_WINDOWS */ -#ifdef HAVE_SETVBUF - setvbuf(stdin, (char *)NULL, _IOLBF, BUFSIZ); - setvbuf(stdout, (char *)NULL, _IOLBF, BUFSIZ); -#endif /* HAVE_SETVBUF */ -#endif /* !MS_WINDOWS */ - /* Leave stderr alone - it should be unbuffered anyway. */ - } -} - - -static void -pymain_header(const _PyCoreConfig *config) -{ - if (config->quiet) { - return; - } - - if (!config->verbose && (RUN_CODE(config) || !stdin_is_interactive(config))) { - return; - } - - fprintf(stderr, "Python %s on %s\n", Py_GetVersion(), Py_GetPlatform()); - if (config->site_import) { - fprintf(stderr, "%s\n", COPYRIGHT); - } -} - - -static _PyInitError -pymain_init_core_argv(_PyCoreConfig *config, _PyCmdline *cmdline) -{ - /* Copy argv to be able to modify it (to force -c/-m) */ - int argc = cmdline->args->argc - _PyOS_optind; - wchar_t **argv; - - if (argc <= 0 || cmdline->argv == NULL) { - /* Ensure at least one (empty) argument is seen */ - static wchar_t *empty_argv[1] = {L""}; - argc = 1; - argv = _Py_wstrlist_copy(1, empty_argv); - } - else { - argv = _Py_wstrlist_copy(argc, &cmdline->argv[_PyOS_optind]); - } - - if (argv == NULL) { - return _Py_INIT_NO_MEMORY(); - } - - wchar_t *arg0 = NULL; - if (config->run_command != NULL) { - /* Force sys.argv[0] = '-c' */ - arg0 = L"-c"; - } - else if (config->run_module != NULL) { - /* Force sys.argv[0] = '-m'*/ - arg0 = L"-m"; - } - if (arg0 != NULL) { - arg0 = _PyMem_RawWcsdup(arg0); - if (arg0 == NULL) { - _Py_wstrlist_clear(argc, argv); - return _Py_INIT_NO_MEMORY(); - } - - assert(argc >= 1); - PyMem_RawFree(argv[0]); - argv[0] = arg0; - } - - config->argc = argc; - config->argv = argv; - return _Py_INIT_OK(); -} - - -PyObject* -_Py_wstrlist_as_pylist(int len, wchar_t **list) -{ - assert(list != NULL || len < 1); - - PyObject *pylist = PyList_New(len); - if (pylist == NULL) { - return NULL; - } - - for (int i = 0; i < len; i++) { - PyObject *v = PyUnicode_FromWideChar(list[i], -1); - if (v == NULL) { - Py_DECREF(pylist); - return NULL; - } - PyList_SET_ITEM(pylist, i, v); - } - return pylist; -} - - -static void -pymain_import_readline(const _PyCoreConfig *config) -{ - if (config->isolated) { - return; - } - if (!config->inspect && RUN_CODE(config)) { - return; - } - if (!isatty(fileno(stdin))) { - return; - } - - PyObject *mod = PyImport_ImportModule("readline"); - if (mod == NULL) { - PyErr_Clear(); - } - else { - Py_DECREF(mod); - } -} - - -static void -pymain_run_startup(_PyCoreConfig *config, PyCompilerFlags *cf) -{ - const char *startup = _PyCoreConfig_GetEnv(config, "PYTHONSTARTUP"); - if (startup == NULL) { - return; - } - - FILE *fp = _Py_fopen(startup, "r"); - if (fp == NULL) { - int save_errno = errno; - PySys_WriteStderr("Could not open PYTHONSTARTUP\n"); - errno = save_errno; - - PyErr_SetFromErrnoWithFilename(PyExc_OSError, - startup); - PyErr_Print(); - return; - } - - (void) PyRun_SimpleFileExFlags(fp, startup, 0, cf); - PyErr_Clear(); - fclose(fp); -} - - -static int -pymain_run_file(_PyCoreConfig *config, PyCompilerFlags *cf) -{ - const wchar_t *filename = config->run_filename; - FILE *fp = _Py_wfopen(filename, L"r"); - if (fp == NULL) { - char *cfilename_buffer; - const char *cfilename; - int err = errno; - cfilename_buffer = _Py_EncodeLocaleRaw(filename, NULL); - if (cfilename_buffer != NULL) - cfilename = cfilename_buffer; - else - cfilename = ""; - fprintf(stderr, "%ls: can't open file '%s': [Errno %d] %s\n", - config->program, cfilename, err, strerror(err)); - PyMem_RawFree(cfilename_buffer); - return 2; - } - - if (config->skip_source_first_line) { - int ch; - /* Push back first newline so line numbers remain the same */ - while ((ch = getc(fp)) != EOF) { - if (ch == '\n') { - (void)ungetc(ch, fp); - break; - } - } - } - - struct _Py_stat_struct sb; - if (_Py_fstat_noraise(fileno(fp), &sb) == 0 && S_ISDIR(sb.st_mode)) { - fprintf(stderr, - "%ls: '%ls' is a directory, cannot continue\n", - config->program, filename); - fclose(fp); - return 1; - } - - /* call pending calls like signal handlers (SIGINT) */ - if (Py_MakePendingCalls() == -1) { - PyErr_Print(); - fclose(fp); - return 1; - } - - PyObject *unicode, *bytes = NULL; - const char *filename_str; - - unicode = PyUnicode_FromWideChar(filename, wcslen(filename)); - if (unicode != NULL) { - bytes = PyUnicode_EncodeFSDefault(unicode); - Py_DECREF(unicode); - } - if (bytes != NULL) { - filename_str = PyBytes_AsString(bytes); - } - else { - PyErr_Clear(); - filename_str = ""; - } - - /* PyRun_AnyFileExFlags(closeit=1) calls fclose(fp) before running code */ - int run = PyRun_AnyFileExFlags(fp, filename_str, 1, cf); - Py_XDECREF(bytes); - return (run != 0); -} - - -static int -pymain_run_stdin(_PyCoreConfig *config, PyCompilerFlags *cf) -{ - if (stdin_is_interactive(config)) { - Py_InspectFlag = 0; /* do exit on SystemExit */ - config->inspect = 0; - pymain_run_startup(config, cf); - pymain_run_interactive_hook(); - } - - /* call pending calls like signal handlers (SIGINT) */ - if (Py_MakePendingCalls() == -1) { - PyErr_Print(); - return 1; - } - - int run = PyRun_AnyFileExFlags(stdin, "", 0, cf); - return (run != 0); -} - - -static void -pymain_repl(_PyCoreConfig *config, PyCompilerFlags *cf, int *exitcode) -{ - /* Check this environment variable at the end, to give programs the - opportunity to set it from Python. */ - if (!Py_InspectFlag && _PyCoreConfig_GetEnv(config, "PYTHONINSPECT")) { - Py_InspectFlag = 1; - config->inspect = 1; - } - - if (!(Py_InspectFlag && stdin_is_interactive(config) && RUN_CODE(config))) { - return; - } - - Py_InspectFlag = 0; - config->inspect = 0; - pymain_run_interactive_hook(); - - int res = PyRun_AnyFileFlags(stdin, "", cf); - *exitcode = (res != 0); -} - - -/* Parse the command line. - Handle --version and --help options directly. */ -static _PyInitError -pymain_parse_cmdline(_PyCoreConfig *config, _PyCmdline *cmdline) -{ - int need_usage = 0; - _PyInitError err; - err = pymain_parse_cmdline_impl(config, cmdline, &need_usage); - if (_Py_INIT_FAILED(err)) { - return err; - } - - if (need_usage) { - pymain_usage(1, config->program); - return _Py_INIT_EXIT(2); - } - return _Py_INIT_OK(); -} - - -/* Parse command line options and environment variables. - This code must not use Python runtime apart PyMem_Raw memory allocator. */ -static _PyInitError -pymain_read_conf_impl(_PyCoreConfig *config, _PyCmdline *cmdline) -{ - _PyInitError err; - - err = pymain_parse_cmdline(config, cmdline); - if (_Py_INIT_FAILED(err)) { - return err; - } - - err = pymain_init_core_argv(config, cmdline); - if (_Py_INIT_FAILED(err)) { - return err; - } - - err = _PyCoreConfig_Read(config); - if (_Py_INIT_FAILED(err)) { - return err; - } - - if (config->use_environment) { - err = cmdline_init_env_warnoptions(config, cmdline); - if (_Py_INIT_FAILED(err)) { - return err; - } - } - - err = config_init_warnoptions(config, cmdline); - if (_Py_INIT_FAILED(err)) { - return err; - } - return _Py_INIT_OK(); -} - - -/* Read the configuration and initialize the LC_CTYPE locale: - enable UTF-8 mode (PEP 540) and/or coerce the C locale (PEP 538). */ -static _PyInitError -pymain_read_conf(_PyCoreConfig *config, _PyCmdline *cmdline) -{ - _PyInitError err; - int init_utf8_mode = Py_UTF8Mode; -#ifdef MS_WINDOWS - int init_legacy_encoding = Py_LegacyWindowsFSEncodingFlag; -#endif - _PyCoreConfig save_config = _PyCoreConfig_INIT; - int locale_coerced = 0; - int loops = 0; - - if (_PyCoreConfig_Copy(&save_config, config) < 0) { - err = _Py_INIT_NO_MEMORY(); - goto done; - } - - /* Set LC_CTYPE to the user preferred locale */ - _Py_SetLocaleFromEnv(LC_CTYPE); - - while (1) { - int utf8_mode = config->utf8_mode; - int encoding_changed = 0; - - /* Watchdog to prevent an infinite loop */ - loops++; - if (loops == 3) { - err = _Py_INIT_ERR("Encoding changed twice while " - "reading the configuration"); - goto done; - } - - /* bpo-34207: Py_DecodeLocale() and Py_EncodeLocale() depend - on Py_UTF8Mode and Py_LegacyWindowsFSEncodingFlag. */ - Py_UTF8Mode = config->utf8_mode; -#ifdef MS_WINDOWS - Py_LegacyWindowsFSEncodingFlag = config->legacy_windows_fs_encoding; -#endif - - err = pymain_init_cmdline_argv(config, cmdline); - if (_Py_INIT_FAILED(err)) { - goto done; - } - - err = pymain_read_conf_impl(config, cmdline); - if (_Py_INIT_FAILED(err)) { - goto done; - } - - /* The legacy C locale assumes ASCII as the default text encoding, which - * causes problems not only for the CPython runtime, but also other - * components like GNU readline. - * - * Accordingly, when the CLI detects it, it attempts to coerce it to a - * more capable UTF-8 based alternative. - * - * See the documentation of the PYTHONCOERCECLOCALE setting for more - * details. - */ - if (config->coerce_c_locale && !locale_coerced) { - locale_coerced = 1; - _Py_CoerceLegacyLocale(config->coerce_c_locale_warn); - encoding_changed = 1; - } - - if (utf8_mode == -1) { - if (config->utf8_mode == 1) { - /* UTF-8 Mode enabled */ - encoding_changed = 1; - } - } - else { - if (config->utf8_mode != utf8_mode) { - encoding_changed = 1; - } - } - - if (!encoding_changed) { - break; - } - - /* Reset the configuration before reading again the configuration, - just keep UTF-8 Mode value. */ - int new_utf8_mode = config->utf8_mode; - int new_coerce_c_locale = config->coerce_c_locale; - if (_PyCoreConfig_Copy(config, &save_config) < 0) { - err = _Py_INIT_NO_MEMORY(); - goto done; - } - pymain_clear_cmdline(cmdline); - const _PyArgv *args = cmdline->args; - memset(cmdline, 0, sizeof(*cmdline)); - cmdline->args = args; - config->utf8_mode = new_utf8_mode; - config->coerce_c_locale = new_coerce_c_locale; - - /* The encoding changed: read again the configuration - with the new encoding */ - } - err = _Py_INIT_OK(); - -done: - _PyCoreConfig_Clear(&save_config); - Py_UTF8Mode = init_utf8_mode ; -#ifdef MS_WINDOWS - Py_LegacyWindowsFSEncodingFlag = init_legacy_encoding; -#endif - return err; -} - - -void -_PyMainInterpreterConfig_Clear(_PyMainInterpreterConfig *config) -{ - Py_CLEAR(config->argv); - Py_CLEAR(config->executable); - Py_CLEAR(config->prefix); - Py_CLEAR(config->base_prefix); - Py_CLEAR(config->exec_prefix); - Py_CLEAR(config->base_exec_prefix); - Py_CLEAR(config->warnoptions); - Py_CLEAR(config->xoptions); - Py_CLEAR(config->module_search_path); - Py_CLEAR(config->pycache_prefix); -} - - static PyObject* -config_copy_attr(PyObject *obj) +mainconfig_copy_attr(PyObject *obj) { if (PyUnicode_Check(obj)) { Py_INCREF(obj); @@ -1385,7 +137,7 @@ _PyMainInterpreterConfig_Copy(_PyMainInterpreterConfig *config, #define COPY_OBJ_ATTR(ATTR) \ do { \ if (config2->ATTR != NULL) { \ - config->ATTR = config_copy_attr(config2->ATTR); \ + config->ATTR = mainconfig_copy_attr(config2->ATTR); \ if (config->ATTR == NULL) { \ return -1; \ } \ @@ -1476,7 +228,7 @@ _PyMainInterpreterConfig_Read(_PyMainInterpreterConfig *main_config, } if (main_config->xoptions == NULL) { - main_config->xoptions = config_create_xoptions_dict(config); + main_config->xoptions = mainconfig_create_xoptions_dict(config); if (main_config->xoptions == NULL) { return _Py_INIT_NO_MEMORY(); } @@ -1532,6 +284,43 @@ _PyMainInterpreterConfig_Read(_PyMainInterpreterConfig *main_config, } +/* --- pymain_init() ---------------------------------------------- */ + +static void +config_clear(_PyCoreConfig *config) +{ + PyMemAllocatorEx old_alloc; + _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); + + _PyCoreConfig_Clear(config); + + PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); +} + + +static _PyInitError +config_read_write(_PyCoreConfig *config, const _PyArgv *args) +{ + _PyInitError err; + + PyMemAllocatorEx old_alloc; + _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); + + _PyCoreConfig_GetGlobalConfig(config); + + err = _PyCoreConfig_ReadFromArgv(config, args); + + PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); + + if (_Py_INIT_FAILED(err)) { + return err; + } + + _PyCoreConfig_Write(config); + return _Py_INIT_OK(); +} + + static _PyInitError pymain_init_python_main(PyInterpreterState *interp) { @@ -1551,6 +340,423 @@ pymain_init_python_main(PyInterpreterState *interp) } +static _PyInitError +pymain_init(const _PyArgv *args, PyInterpreterState **interp_p) +{ + _PyInitError err; + + err = _PyRuntime_Initialize(); + if (_Py_INIT_FAILED(err)) { + return err; + } + + /* 754 requires that FP exceptions run in "no stop" mode by default, + * and until C vendors implement C99's ways to control FP exceptions, + * Python requires non-stop mode. Alas, some platforms enable FP + * exceptions by default. Here we disable them. + */ +#ifdef __FreeBSD__ + fedisableexcept(FE_OVERFLOW); +#endif + + _PyCoreConfig local_config = _PyCoreConfig_INIT; + _PyCoreConfig *config = &local_config; + + err = config_read_write(config, args); + if (_Py_INIT_FAILED(err)) { + goto done; + } + + PyInterpreterState *interp; + err = _Py_InitializeCore(&interp, config); + if (_Py_INIT_FAILED(err)) { + goto done; + } + *interp_p = interp; + + err = pymain_init_python_main(interp); + if (_Py_INIT_FAILED(err)) { + goto done; + } + + err = _Py_INIT_OK(); + +done: + config_clear(config); + return err; +} + + +/* --- pymain_run_python() ---------------------------------------- */ + +/* Non-zero if filename, command (-c) or module (-m) is set + on the command line */ +#define RUN_CODE(config) \ + (config->run_command != NULL || config->run_filename != NULL \ + || config->run_module != NULL) + +/* Return non-zero is stdin is a TTY or if -i command line option is used */ +static int +stdin_is_interactive(const _PyCoreConfig *config) +{ + return (isatty(fileno(stdin)) || config->interactive); +} + + +static PyObject * +pymain_get_importer(const wchar_t *filename) +{ + PyObject *sys_path0 = NULL, *importer; + + sys_path0 = PyUnicode_FromWideChar(filename, wcslen(filename)); + if (sys_path0 == NULL) { + goto error; + } + + importer = PyImport_GetImporter(sys_path0); + if (importer == NULL) { + goto error; + } + + if (importer == Py_None) { + Py_DECREF(sys_path0); + Py_DECREF(importer); + return NULL; + } + + Py_DECREF(importer); + return sys_path0; + +error: + Py_XDECREF(sys_path0); + PySys_WriteStderr("Failed checking if argv[0] is an import path entry\n"); + PyErr_Print(); + return NULL; +} + + +static int +pymain_sys_path_add_path0(PyInterpreterState *interp, PyObject *path0) +{ + _Py_IDENTIFIER(path); + PyObject *sys_path; + PyObject *sysdict = interp->sysdict; + if (sysdict != NULL) { + sys_path = _PyDict_GetItemIdWithError(sysdict, &PyId_path); + if (sys_path == NULL && PyErr_Occurred()) { + goto error; + } + } + else { + sys_path = NULL; + } + if (sys_path == NULL) { + PyErr_SetString(PyExc_RuntimeError, "unable to get sys.path"); + goto error; + } + + if (PyList_Insert(sys_path, 0, path0)) { + goto error; + } + return 0; + +error: + PyErr_Print(); + return -1; +} + + +static void +pymain_header(const _PyCoreConfig *config) +{ + if (config->quiet) { + return; + } + + if (!config->verbose && (RUN_CODE(config) || !stdin_is_interactive(config))) { + return; + } + + fprintf(stderr, "Python %s on %s\n", Py_GetVersion(), Py_GetPlatform()); + if (config->site_import) { + fprintf(stderr, "%s\n", COPYRIGHT); + } +} + + +static void +pymain_import_readline(const _PyCoreConfig *config) +{ + if (config->isolated) { + return; + } + if (!config->inspect && RUN_CODE(config)) { + return; + } + if (!isatty(fileno(stdin))) { + return; + } + + PyObject *mod = PyImport_ImportModule("readline"); + if (mod == NULL) { + PyErr_Clear(); + } + else { + Py_DECREF(mod); + } +} + + +static int +pymain_run_command(wchar_t *command, PyCompilerFlags *cf) +{ + PyObject *unicode, *bytes; + int ret; + + unicode = PyUnicode_FromWideChar(command, -1); + if (unicode == NULL) { + goto error; + } + + bytes = PyUnicode_AsUTF8String(unicode); + Py_DECREF(unicode); + if (bytes == NULL) { + goto error; + } + + ret = PyRun_SimpleStringFlags(PyBytes_AsString(bytes), cf); + Py_DECREF(bytes); + return (ret != 0); + +error: + PySys_WriteStderr("Unable to decode the command from the command line:\n"); + PyErr_Print(); + return 1; +} + + +static int +pymain_run_module(const wchar_t *modname, int set_argv0) +{ + PyObject *module, *runpy, *runmodule, *runargs, *result; + runpy = PyImport_ImportModule("runpy"); + if (runpy == NULL) { + fprintf(stderr, "Could not import runpy module\n"); + PyErr_Print(); + return -1; + } + runmodule = PyObject_GetAttrString(runpy, "_run_module_as_main"); + if (runmodule == NULL) { + fprintf(stderr, "Could not access runpy._run_module_as_main\n"); + PyErr_Print(); + Py_DECREF(runpy); + return -1; + } + module = PyUnicode_FromWideChar(modname, wcslen(modname)); + if (module == NULL) { + fprintf(stderr, "Could not convert module name to unicode\n"); + PyErr_Print(); + Py_DECREF(runpy); + Py_DECREF(runmodule); + return -1; + } + runargs = Py_BuildValue("(Oi)", module, set_argv0); + if (runargs == NULL) { + fprintf(stderr, + "Could not create arguments for runpy._run_module_as_main\n"); + PyErr_Print(); + Py_DECREF(runpy); + Py_DECREF(runmodule); + Py_DECREF(module); + return -1; + } + result = PyObject_Call(runmodule, runargs, NULL); + if (result == NULL) { + PyErr_Print(); + } + Py_DECREF(runpy); + Py_DECREF(runmodule); + Py_DECREF(module); + Py_DECREF(runargs); + if (result == NULL) { + return -1; + } + Py_DECREF(result); + return 0; +} + + +static int +pymain_run_file(_PyCoreConfig *config, PyCompilerFlags *cf) +{ + const wchar_t *filename = config->run_filename; + FILE *fp = _Py_wfopen(filename, L"r"); + if (fp == NULL) { + char *cfilename_buffer; + const char *cfilename; + int err = errno; + cfilename_buffer = _Py_EncodeLocaleRaw(filename, NULL); + if (cfilename_buffer != NULL) + cfilename = cfilename_buffer; + else + cfilename = ""; + fprintf(stderr, "%ls: can't open file '%s': [Errno %d] %s\n", + config->program, cfilename, err, strerror(err)); + PyMem_RawFree(cfilename_buffer); + return 2; + } + + if (config->skip_source_first_line) { + int ch; + /* Push back first newline so line numbers remain the same */ + while ((ch = getc(fp)) != EOF) { + if (ch == '\n') { + (void)ungetc(ch, fp); + break; + } + } + } + + struct _Py_stat_struct sb; + if (_Py_fstat_noraise(fileno(fp), &sb) == 0 && S_ISDIR(sb.st_mode)) { + fprintf(stderr, + "%ls: '%ls' is a directory, cannot continue\n", + config->program, filename); + fclose(fp); + return 1; + } + + /* call pending calls like signal handlers (SIGINT) */ + if (Py_MakePendingCalls() == -1) { + PyErr_Print(); + fclose(fp); + return 1; + } + + PyObject *unicode, *bytes = NULL; + const char *filename_str; + + unicode = PyUnicode_FromWideChar(filename, wcslen(filename)); + if (unicode != NULL) { + bytes = PyUnicode_EncodeFSDefault(unicode); + Py_DECREF(unicode); + } + if (bytes != NULL) { + filename_str = PyBytes_AsString(bytes); + } + else { + PyErr_Clear(); + filename_str = ""; + } + + /* PyRun_AnyFileExFlags(closeit=1) calls fclose(fp) before running code */ + int run = PyRun_AnyFileExFlags(fp, filename_str, 1, cf); + Py_XDECREF(bytes); + return (run != 0); +} + + +static void +pymain_run_startup(_PyCoreConfig *config, PyCompilerFlags *cf) +{ + const char *startup = _PyCoreConfig_GetEnv(config, "PYTHONSTARTUP"); + if (startup == NULL) { + return; + } + + FILE *fp = _Py_fopen(startup, "r"); + if (fp == NULL) { + int save_errno = errno; + PySys_WriteStderr("Could not open PYTHONSTARTUP\n"); + errno = save_errno; + + PyErr_SetFromErrnoWithFilename(PyExc_OSError, + startup); + PyErr_Print(); + return; + } + + (void) PyRun_SimpleFileExFlags(fp, startup, 0, cf); + PyErr_Clear(); + fclose(fp); +} + + +static void +pymain_run_interactive_hook(void) +{ + PyObject *sys, *hook, *result; + sys = PyImport_ImportModule("sys"); + if (sys == NULL) { + goto error; + } + + hook = PyObject_GetAttrString(sys, "__interactivehook__"); + Py_DECREF(sys); + if (hook == NULL) { + PyErr_Clear(); + return; + } + + result = _PyObject_CallNoArg(hook); + Py_DECREF(hook); + if (result == NULL) { + goto error; + } + Py_DECREF(result); + + return; + +error: + PySys_WriteStderr("Failed calling sys.__interactivehook__\n"); + PyErr_Print(); +} + + +static int +pymain_run_stdin(_PyCoreConfig *config, PyCompilerFlags *cf) +{ + if (stdin_is_interactive(config)) { + Py_InspectFlag = 0; /* do exit on SystemExit */ + config->inspect = 0; + pymain_run_startup(config, cf); + pymain_run_interactive_hook(); + } + + /* call pending calls like signal handlers (SIGINT) */ + if (Py_MakePendingCalls() == -1) { + PyErr_Print(); + return 1; + } + + int run = PyRun_AnyFileExFlags(stdin, "", 0, cf); + return (run != 0); +} + + +static void +pymain_repl(_PyCoreConfig *config, PyCompilerFlags *cf, int *exitcode) +{ + /* Check this environment variable at the end, to give programs the + opportunity to set it from Python. */ + if (!Py_InspectFlag && _PyCoreConfig_GetEnv(config, "PYTHONINSPECT")) { + Py_InspectFlag = 1; + config->inspect = 1; + } + + if (!(Py_InspectFlag && stdin_is_interactive(config) && RUN_CODE(config))) { + return; + } + + Py_InspectFlag = 0; + config->inspect = 0; + pymain_run_interactive_hook(); + + int res = PyRun_AnyFileFlags(stdin, "", cf); + *exitcode = (res != 0); +} + + static _PyInitError pymain_run_python(PyInterpreterState *interp, int *exitcode) { @@ -1620,129 +826,58 @@ done: } -static _PyInitError -pymain_cmdline_impl(_PyCoreConfig *config, _PyCmdline *cmdline) +/* --- pymain_main() ---------------------------------------------- */ + +static void +pymain_free(void) { - _PyInitError err; + _PyImport_Fini2(); - err = _PyRuntime_Initialize(); - if (_Py_INIT_FAILED(err)) { - return err; - } - - err = pymain_read_conf(config, cmdline); - if (_Py_INIT_FAILED(err)) { - return err; - } - - if (cmdline->print_help) { - pymain_usage(0, config->program); - return _Py_INIT_EXIT(0); - } - - if (cmdline->print_version) { - printf("Python %s\n", - (cmdline->print_version >= 2) ? Py_GetVersion() : PY_VERSION); - return _Py_INIT_EXIT(0); - } - - /* For Py_GetArgcArgv(). Cleared by pymain_free(). */ - orig_argv = _Py_wstrlist_copy(cmdline->args->argc, cmdline->argv); - if (orig_argv == NULL) { - return _Py_INIT_NO_MEMORY(); - } - orig_argc = cmdline->args->argc; - return _Py_INIT_OK(); -} - - -/* Read the configuration into _PyCoreConfig and _PyMain, initialize the - LC_CTYPE locale and Py_DecodeLocale(). - - Configuration: - - * Command line arguments - * Environment variables - * Py_xxx global configuration variables - - _PyCmdline is a temporary structure used to prioritize these - variables. */ -static _PyInitError -pymain_cmdline(_PyArgv *args, _PyCoreConfig *config) -{ - /* Force default allocator, since pymain_free() and pymain_clear_config() - must use the same allocator than this function. */ - PyMemAllocatorEx old_alloc; - _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); -#ifdef Py_DEBUG - PyMemAllocatorEx default_alloc; - PyMem_GetAllocator(PYMEM_DOMAIN_RAW, &default_alloc); -#endif - - _PyCmdline cmdline; - memset(&cmdline, 0, sizeof(cmdline)); - cmdline.args = args; - - _PyInitError err = pymain_cmdline_impl(config, &cmdline); - - pymain_clear_cmdline(&cmdline); - -#ifdef Py_DEBUG - /* Make sure that PYMEM_DOMAIN_RAW has not been modified */ - PyMemAllocatorEx cur_alloc; - PyMem_GetAllocator(PYMEM_DOMAIN_RAW, &cur_alloc); - assert(memcmp(&cur_alloc, &default_alloc, sizeof(cur_alloc)) == 0); -#endif - PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); - return err; -} - - -static _PyInitError -pymain_init(_PyArgv *args, PyInterpreterState **interp_p) -{ - _PyInitError err; - - /* 754 requires that FP exceptions run in "no stop" mode by default, - * and until C vendors implement C99's ways to control FP exceptions, - * Python requires non-stop mode. Alas, some platforms enable FP - * exceptions by default. Here we disable them. + /* Free global variables which cannot be freed in Py_Finalize(): + configuration options set before Py_Initialize() which should + remain valid after Py_Finalize(), since + Py_Initialize()-Py_Finalize() can be called multiple times. */ + _PyPathConfig_ClearGlobal(); + _Py_ClearStandardStreamEncoding(); + _Py_ClearArgcArgv(); +#ifdef __INSURE__ + /* Insure++ is a memory analysis tool that aids in discovering + * memory leaks and other memory problems. On Python exit, the + * interned string dictionaries are flagged as being in use at exit + * (which it is). Under normal circumstances, this is fine because + * the memory will be automatically reclaimed by the system. Under + * memory debugging, it's a huge source of useless noise, so we + * trade off slower shutdown for less distraction in the memory + * reports. -baw */ -#ifdef __FreeBSD__ - fedisableexcept(FE_OVERFLOW); -#endif + _Py_ReleaseInternedUnicodeStrings(); +#endif /* __INSURE__ */ +} - _PyCoreConfig local_config = _PyCoreConfig_INIT; - _PyCoreConfig *config = &local_config; - _PyCoreConfig_GetGlobalConfig(config); - - err = pymain_cmdline(args, config); - if (_Py_INIT_FAILED(err)) { - goto done; +static int +exit_sigint(void) +{ + /* bpo-1054041: We need to exit via the + * SIG_DFL handler for SIGINT if KeyboardInterrupt went unhandled. + * If we don't, a calling process such as a shell may not know + * about the user's ^C. https://www.cons.org/cracauer/sigint.html */ +#if defined(HAVE_GETPID) && !defined(MS_WINDOWS) + if (PyOS_setsig(SIGINT, SIG_DFL) == SIG_ERR) { + perror("signal"); /* Impossible in normal environments. */ + } else { + kill(getpid(), SIGINT); } - - _PyCoreConfig_SetGlobalConfig(config); - - pymain_init_stdio(config); - - PyInterpreterState *interp; - err = _Py_InitializeCore(&interp, config); - if (_Py_INIT_FAILED(err)) { - goto done; - } - *interp_p = interp; - - err = pymain_init_python_main(interp); - if (_Py_INIT_FAILED(err)) { - goto done; - } - - err = _Py_INIT_OK(); - -done: - pymain_clear_config(config); - return err; + /* If setting SIG_DFL failed, or kill failed to terminate us, + * there isn't much else we can do aside from an error code. */ +#endif /* HAVE_GETPID && !MS_WINDOWS */ +#ifdef MS_WINDOWS + /* cmd.exe detects this, prints ^C, and offers to terminate. */ + /* https://msdn.microsoft.com/en-us/library/cc704588.aspx */ + return STATUS_CONTROL_C_EXIT; +#else + return SIGINT + 128; +#endif /* !MS_WINDOWS */ } @@ -1772,26 +907,7 @@ pymain_main(_PyArgv *args) pymain_free(); if (_Py_UnhandledKeyboardInterrupt) { - /* https://bugs.python.org/issue1054041 - We need to exit via the - * SIG_DFL handler for SIGINT if KeyboardInterrupt went unhandled. - * If we don't, a calling process such as a shell may not know - * about the user's ^C. https://www.cons.org/cracauer/sigint.html */ -#if defined(HAVE_GETPID) && !defined(MS_WINDOWS) - if (PyOS_setsig(SIGINT, SIG_DFL) == SIG_ERR) { - perror("signal"); /* Impossible in normal environments. */ - } else { - kill(getpid(), SIGINT); - } - /* If setting SIG_DFL failed, or kill failed to terminate us, - * there isn't much else we can do aside from an error code. */ -#endif /* HAVE_GETPID && !MS_WINDOWS */ -#ifdef MS_WINDOWS - /* cmd.exe detects this, prints ^C, and offers to terminate. */ - /* https://msdn.microsoft.com/en-us/library/cc704588.aspx */ - exitcode = STATUS_CONTROL_C_EXIT; -#else - exitcode = SIGINT + 128; -#endif /* !MS_WINDOWS */ + exitcode = exit_sigint(); } return exitcode; @@ -1821,21 +937,6 @@ _Py_UnixMain(int argc, char **argv) return pymain_main(&args); } - -/* this is gonna seem *real weird*, but if you put some other code between - Py_Main() and Py_GetArgcArgv() you will need to adjust the test in the - while statement in Misc/gdbinit:ppystack */ - -/* Make the *original* argc/argv available to other modules. - This is rare, but it is needed by the secureware extension. */ - -void -Py_GetArgcArgv(int *argc, wchar_t ***argv) -{ - *argc = orig_argc; - *argv = orig_argv; -} - #ifdef __cplusplus } #endif diff --git a/Python/coreconfig.c b/Python/coreconfig.c index d9b30130c93..47dbe66d3eb 100644 --- a/Python/coreconfig.c +++ b/Python/coreconfig.c @@ -1,35 +1,116 @@ #include "Python.h" +#include "osdefs.h" /* DELIM */ #include "pycore_coreconfig.h" #include "pycore_fileutils.h" +#include "pycore_getopt.h" #include "pycore_pylifecycle.h" #include "pycore_pymem.h" #include "pycore_pathconfig.h" -#include "pycore_pystate.h" -#include +#include /* setlocale() */ #ifdef HAVE_LANGINFO_H -# include +# include /* nl_langinfo(CODESET) */ #endif - -#include /* setlocale() */ -#ifdef HAVE_LANGINFO_H -#include /* nl_langinfo(CODESET) */ +#if defined(MS_WINDOWS) || defined(__CYGWIN__) +# include /* GetACP() */ +# ifdef HAVE_IO_H +# include +# endif +# ifdef HAVE_FCNTL_H +# include /* O_BINARY */ +# endif #endif -#define DECODE_LOCALE_ERR(NAME, LEN) \ - (((LEN) == -2) \ - ? _Py_INIT_USER_ERR("cannot decode " NAME) \ - : _Py_INIT_NO_MEMORY()) +/* --- Command line options --------------------------------------- */ + +#define PROGRAM_OPTS L"bBc:dEhiIJm:OqRsStuvVW:xX:?" + +static const _PyOS_LongOption longoptions[] = { + {L"check-hash-based-pycs", 1, 0}, + {NULL, 0, 0}, +}; + +/* Short usage message (with %s for argv0) */ +static const char usage_line[] = +"usage: %ls [option] ... [-c cmd | -m mod | file | -] [arg] ...\n"; + +/* Long usage message, split into parts < 512 bytes */ +static const char usage_1[] = "\ +Options and arguments (and corresponding environment variables):\n\ +-b : issue warnings about str(bytes_instance), str(bytearray_instance)\n\ + and comparing bytes/bytearray with str. (-bb: issue errors)\n\ +-B : don't write .pyc files on import; also PYTHONDONTWRITEBYTECODE=x\n\ +-c cmd : program passed in as string (terminates option list)\n\ +-d : debug output from parser; also PYTHONDEBUG=x\n\ +-E : ignore PYTHON* environment variables (such as PYTHONPATH)\n\ +-h : print this help message and exit (also --help)\n\ +"; +static const char usage_2[] = "\ +-i : inspect interactively after running script; forces a prompt even\n\ + if stdin does not appear to be a terminal; also PYTHONINSPECT=x\n\ +-I : isolate Python from the user's environment (implies -E and -s)\n\ +-m mod : run library module as a script (terminates option list)\n\ +-O : remove assert and __debug__-dependent statements; add .opt-1 before\n\ + .pyc extension; also PYTHONOPTIMIZE=x\n\ +-OO : do -O changes and also discard docstrings; add .opt-2 before\n\ + .pyc extension\n\ +-q : don't print version and copyright messages on interactive startup\n\ +-s : don't add user site directory to sys.path; also PYTHONNOUSERSITE\n\ +-S : don't imply 'import site' on initialization\n\ +"; +static const char usage_3[] = "\ +-u : force the stdout and stderr streams to be unbuffered;\n\ + this option has no effect on stdin; also PYTHONUNBUFFERED=x\n\ +-v : verbose (trace import statements); also PYTHONVERBOSE=x\n\ + can be supplied multiple times to increase verbosity\n\ +-V : print the Python version number and exit (also --version)\n\ + when given twice, print more information about the build\n\ +-W arg : warning control; arg is action:message:category:module:lineno\n\ + also PYTHONWARNINGS=arg\n\ +-x : skip first line of source, allowing use of non-Unix forms of #!cmd\n\ +-X opt : set implementation-specific option\n\ +--check-hash-based-pycs always|default|never:\n\ + control how Python invalidates hash-based .pyc files\n\ +"; +static const char usage_4[] = "\ +file : program read from script file\n\ +- : program read from stdin (default; interactive mode if a tty)\n\ +arg ...: arguments passed to program in sys.argv[1:]\n\n\ +Other environment variables:\n\ +PYTHONSTARTUP: file executed on interactive startup (no default)\n\ +PYTHONPATH : '%lc'-separated list of directories prefixed to the\n\ + default module search path. The result is sys.path.\n\ +"; +static const char usage_5[] = +"PYTHONHOME : alternate directory (or %lc).\n" +" The default module search path uses %s.\n" +"PYTHONCASEOK : ignore case in 'import' statements (Windows).\n" +"PYTHONIOENCODING: Encoding[:errors] used for stdin/stdout/stderr.\n" +"PYTHONFAULTHANDLER: dump the Python traceback on fatal errors.\n"; +static const char usage_6[] = +"PYTHONHASHSEED: if this variable is set to 'random', a random value is used\n" +" to seed the hashes of str, bytes and datetime objects. It can also be\n" +" set to an integer in the range [0,4294967295] to get hash values with a\n" +" predictable seed.\n" +"PYTHONMALLOC: set the Python memory allocators and/or install debug hooks\n" +" on Python memory allocators. Use PYTHONMALLOC=debug to install debug\n" +" hooks.\n" +"PYTHONCOERCECLOCALE: if this variable is set to 0, it disables the locale\n" +" coercion behavior. Use PYTHONCOERCECLOCALE=warn to request display of\n" +" locale coercion and locale compatibility warnings on stderr.\n" +"PYTHONBREAKPOINT: if this variable is set to 0, it disables the default\n" +" debugger. It can be set to the callable of your debugger of choice.\n" +"PYTHONDEVMODE: enable the development mode.\n" +"PYTHONPYCACHEPREFIX: root directory for bytecode cache (pyc) files.\n"; + +#if defined(MS_WINDOWS) +# define PYTHONHOMEHELP "\\python{major}{minor}" +#else +# define PYTHONHOMEHELP "/lib/pythonX.X" +#endif -/* Global configuration variables */ - -/* The filesystem encoding is chosen by config_init_fs_encoding(), - see also initfsencoding(). */ -const char *Py_FileSystemDefaultEncoding = NULL; -int Py_HasFileSystemDefaultEncoding = 0; -const char *Py_FileSystemDefaultEncodeErrors = NULL; -static int _Py_HasFileSystemDefaultEncodeErrors = 0; +/* --- Global configuration variables ----------------------------- */ /* UTF-8 mode (PEP 540): if equals to 1, use the UTF-8 encoding, and change stdin and stdout error handler to "surrogateescape". It is equal to @@ -55,6 +136,13 @@ int Py_LegacyWindowsFSEncodingFlag = 0; /* Uses mbcs instead of utf-8 */ int Py_LegacyWindowsStdioFlag = 0; /* Uses FileIO instead of WindowsConsoleIO */ #endif +/* The filesystem encoding is chosen by config_init_fs_encoding(), + see also initfsencoding(). */ +const char *Py_FileSystemDefaultEncoding = NULL; +int Py_HasFileSystemDefaultEncoding = 0; +const char *Py_FileSystemDefaultEncodeErrors = NULL; +static int _Py_HasFileSystemDefaultEncodeErrors = 0; + PyObject * _Py_GetGlobalVariablesAsDict(void) @@ -128,6 +216,8 @@ fail: } +/* --- _Py_wstrlist ----------------------------------------------- */ + void _Py_wstrlist_clear(int len, wchar_t **list) { @@ -139,7 +229,7 @@ _Py_wstrlist_clear(int len, wchar_t **list) wchar_t** -_Py_wstrlist_copy(int len, wchar_t **list) +_Py_wstrlist_copy(int len, wchar_t * const *list) { assert((len > 0 && list != NULL) || len == 0); size_t size = len * sizeof(list[0]); @@ -159,6 +249,53 @@ _Py_wstrlist_copy(int len, wchar_t **list) } +_PyInitError +_Py_wstrlist_append(int *len, wchar_t ***list, const wchar_t *str) +{ + if (*len == INT_MAX) { + /* len+1 would overflow */ + return _Py_INIT_NO_MEMORY(); + } + wchar_t *str2 = _PyMem_RawWcsdup(str); + if (str2 == NULL) { + return _Py_INIT_NO_MEMORY(); + } + + size_t size = (*len + 1) * sizeof(list[0]); + wchar_t **list2 = (wchar_t **)PyMem_RawRealloc(*list, size); + if (list2 == NULL) { + PyMem_RawFree(str2); + return _Py_INIT_NO_MEMORY(); + } + list2[*len] = str2; + *list = list2; + (*len)++; + return _Py_INIT_OK(); +} + + +PyObject* +_Py_wstrlist_as_pylist(int len, wchar_t **list) +{ + assert(list != NULL || len < 1); + + PyObject *pylist = PyList_New(len); + if (pylist == NULL) { + return NULL; + } + + for (int i = 0; i < len; i++) { + PyObject *v = PyUnicode_FromWideChar(list[i], -1); + if (v == NULL) { + Py_DECREF(pylist); + return NULL; + } + PyList_SET_ITEM(pylist, i, v); + } + return pylist; +} + + void _Py_ClearFileSystemEncoding(void) { @@ -173,6 +310,8 @@ _Py_ClearFileSystemEncoding(void) } +/* --- File system encoding/errors -------------------------------- */ + /* Set Py_FileSystemDefaultEncoding and Py_FileSystemDefaultEncodeErrors global configuration variables. */ int @@ -200,6 +339,8 @@ _Py_SetFileSystemEncoding(const char *encoding, const char *errors) } +/* --- Py_SetStandardStreamEncoding() ----------------------------- */ + /* Helper to allow an embedding application to override the normal * mechanism that attempts to figure out an appropriate IO encoding */ @@ -282,6 +423,68 @@ _Py_ClearStandardStreamEncoding(void) } +/* --- Py_GetArgcArgv() ------------------------------------------- */ + +/* For Py_GetArgcArgv(); set by _Py_SetArgcArgv() */ +static int orig_argc = 0; +static wchar_t **orig_argv = NULL; + + +void +_Py_ClearArgcArgv(void) +{ + PyMemAllocatorEx old_alloc; + _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); + + _Py_wstrlist_clear(orig_argc, orig_argv); + orig_argc = 0; + orig_argv = NULL; + + PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); +} + + +int +_Py_SetArgcArgv(int argc, wchar_t * const *argv) +{ + int res; + + PyMemAllocatorEx old_alloc; + _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); + + wchar_t **argv_copy = _Py_wstrlist_copy(argc, argv); + if (argv_copy != NULL) { + _Py_ClearArgcArgv(); + orig_argc = argc; + orig_argv = argv_copy; + res = 0; + } + else { + res = -1; + } + + PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); + return res; +} + + +/* Make the *original* argc/argv available to other modules. + This is rare, but it is needed by the secureware extension. */ +void +Py_GetArgcArgv(int *argc, wchar_t ***argv) +{ + *argc = orig_argc; + *argv = orig_argv; +} + + +/* --- _PyCoreConfig ---------------------------------------------- */ + +#define DECODE_LOCALE_ERR(NAME, LEN) \ + (((LEN) == -2) \ + ? _Py_INIT_USER_ERR("cannot decode " NAME) \ + : _Py_INIT_NO_MEMORY()) + /* Free memory allocated in config, but don't clear all attributes */ void _PyCoreConfig_Clear(_PyCoreConfig *config) @@ -1469,6 +1672,60 @@ _PyCoreConfig_Read(_PyCoreConfig *config) } +static void +config_init_stdio(const _PyCoreConfig *config) +{ +#if defined(MS_WINDOWS) || defined(__CYGWIN__) + /* don't translate newlines (\r\n <=> \n) */ + _setmode(fileno(stdin), O_BINARY); + _setmode(fileno(stdout), O_BINARY); + _setmode(fileno(stderr), O_BINARY); +#endif + + if (!config->buffered_stdio) { +#ifdef HAVE_SETVBUF + setvbuf(stdin, (char *)NULL, _IONBF, BUFSIZ); + setvbuf(stdout, (char *)NULL, _IONBF, BUFSIZ); + setvbuf(stderr, (char *)NULL, _IONBF, BUFSIZ); +#else /* !HAVE_SETVBUF */ + setbuf(stdin, (char *)NULL); + setbuf(stdout, (char *)NULL); + setbuf(stderr, (char *)NULL); +#endif /* !HAVE_SETVBUF */ + } + else if (config->interactive) { +#ifdef MS_WINDOWS + /* Doesn't have to have line-buffered -- use unbuffered */ + /* Any set[v]buf(stdin, ...) screws up Tkinter :-( */ + setvbuf(stdout, (char *)NULL, _IONBF, BUFSIZ); +#else /* !MS_WINDOWS */ +#ifdef HAVE_SETVBUF + setvbuf(stdin, (char *)NULL, _IOLBF, BUFSIZ); + setvbuf(stdout, (char *)NULL, _IOLBF, BUFSIZ); +#endif /* HAVE_SETVBUF */ +#endif /* !MS_WINDOWS */ + /* Leave stderr alone - it should be unbuffered anyway. */ + } +} + + +/* Write the configuration: + + - coerce the LC_CTYPE locale (PEP 538) + - UTF-8 mode (PEP 540) + - set Py_xxx global configuration variables + - initialize C standard streams (stdin, stdout, stderr) */ +void +_PyCoreConfig_Write(const _PyCoreConfig *config) +{ + if (config->coerce_c_locale) { + _Py_CoerceLegacyLocale(config->coerce_c_locale_warn); + } + _PyCoreConfig_SetGlobalConfig(config); + config_init_stdio(config); +} + + PyObject * _PyCoreConfig_AsDict(const _PyCoreConfig *config) { @@ -1586,3 +1843,681 @@ fail: #undef SET_ITEM_WSTR #undef SET_ITEM_WSTRLIST } + + +/* --- _PyCmdline ------------------------------------------------- */ + +typedef struct { + const _PyArgv *args; + wchar_t **argv; + int nwarnoption; /* Number of -W command line options */ + wchar_t **warnoptions; /* Command line -W options */ + int nenv_warnoption; /* Number of PYTHONWARNINGS environment variables */ + wchar_t **env_warnoptions; /* PYTHONWARNINGS environment variables */ + int print_help; /* -h, -? options */ + int print_version; /* -V option */ +} _PyCmdline; + + +static void +cmdline_clear(_PyCmdline *cmdline) +{ + _Py_wstrlist_clear(cmdline->nwarnoption, cmdline->warnoptions); + cmdline->nwarnoption = 0; + cmdline->warnoptions = NULL; + + _Py_wstrlist_clear(cmdline->nenv_warnoption, cmdline->env_warnoptions); + cmdline->nenv_warnoption = 0; + cmdline->env_warnoptions = NULL; + + if (cmdline->args->use_bytes_argv && cmdline->argv != NULL) { + _Py_wstrlist_clear(cmdline->args->argc, cmdline->argv); + } + cmdline->argv = NULL; +} + + +static _PyInitError +cmdline_decode_argv(_PyCmdline *cmdline) +{ + assert(cmdline->argv == NULL); + + const _PyArgv *args = cmdline->args; + + if (args->use_bytes_argv) { + /* +1 for a the NULL terminator */ + size_t size = sizeof(wchar_t*) * (args->argc + 1); + wchar_t** argv = (wchar_t **)PyMem_RawMalloc(size); + if (argv == NULL) { + return _Py_INIT_NO_MEMORY(); + } + + for (int i = 0; i < args->argc; i++) { + size_t len; + wchar_t *arg = Py_DecodeLocale(args->bytes_argv[i], &len); + if (arg == NULL) { + _Py_wstrlist_clear(i, argv); + return DECODE_LOCALE_ERR("command line arguments", + (Py_ssize_t)len); + } + argv[i] = arg; + } + argv[args->argc] = NULL; + + cmdline->argv = argv; + } + else { + cmdline->argv = args->wchar_argv; + } + return _Py_INIT_OK(); +} + + +/* --- _PyCoreConfig command line parser -------------------------- */ + +/* Parse the command line arguments */ +static _PyInitError +config_parse_cmdline(_PyCoreConfig *config, _PyCmdline *cmdline, + int *need_usage) +{ + _PyInitError err; + _PyOS_ResetGetOpt(); + do { + int longindex = -1; + int c = _PyOS_GetOpt(cmdline->args->argc, cmdline->argv, PROGRAM_OPTS, + longoptions, &longindex); + if (c == EOF) { + break; + } + + if (c == 'c') { + /* -c is the last option; following arguments + that look like options are left for the + command to interpret. */ + size_t len = wcslen(_PyOS_optarg) + 1 + 1; + wchar_t *command = PyMem_RawMalloc(sizeof(wchar_t) * len); + if (command == NULL) { + return _Py_INIT_NO_MEMORY(); + } + memcpy(command, _PyOS_optarg, (len - 2) * sizeof(wchar_t)); + command[len - 2] = '\n'; + command[len - 1] = 0; + config->run_command = command; + break; + } + + if (c == 'm') { + /* -m is the last option; following arguments + that look like options are left for the + module to interpret. */ + config->run_module = _PyMem_RawWcsdup(_PyOS_optarg); + if (config->run_module == NULL) { + return _Py_INIT_NO_MEMORY(); + } + break; + } + + switch (c) { + case 0: + // Handle long option. + assert(longindex == 0); // Only one long option now. + if (!wcscmp(_PyOS_optarg, L"always")) { + config->_check_hash_pycs_mode = "always"; + } else if (!wcscmp(_PyOS_optarg, L"never")) { + config->_check_hash_pycs_mode = "never"; + } else if (!wcscmp(_PyOS_optarg, L"default")) { + config->_check_hash_pycs_mode = "default"; + } else { + fprintf(stderr, "--check-hash-based-pycs must be one of " + "'default', 'always', or 'never'\n"); + *need_usage = 1; + return _Py_INIT_OK(); + } + break; + + case 'b': + config->bytes_warning++; + break; + + case 'd': + config->parser_debug++; + break; + + case 'i': + config->inspect++; + config->interactive++; + break; + + case 'I': + config->isolated++; + break; + + /* case 'J': reserved for Jython */ + + case 'O': + config->optimization_level++; + break; + + case 'B': + config->write_bytecode = 0; + break; + + case 's': + config->user_site_directory = 0; + break; + + case 'S': + config->site_import = 0; + break; + + case 'E': + config->use_environment = 0; + break; + + case 't': + /* ignored for backwards compatibility */ + break; + + case 'u': + config->buffered_stdio = 0; + break; + + case 'v': + config->verbose++; + break; + + case 'x': + config->skip_source_first_line = 1; + break; + + case 'h': + case '?': + cmdline->print_help++; + break; + + case 'V': + cmdline->print_version++; + break; + + case 'W': + err = _Py_wstrlist_append(&cmdline->nwarnoption, + &cmdline->warnoptions, + _PyOS_optarg); + if (_Py_INIT_FAILED(err)) { + return err; + } + break; + + case 'X': + err = _Py_wstrlist_append(&config->nxoption, + &config->xoptions, + _PyOS_optarg); + if (_Py_INIT_FAILED(err)) { + return err; + } + break; + + case 'q': + config->quiet++; + break; + + case 'R': + config->use_hash_seed = 0; + break; + + /* This space reserved for other options */ + + default: + /* unknown argument: parsing failed */ + *need_usage = 1; + return _Py_INIT_OK(); + } + } while (1); + + if (config->run_command == NULL && config->run_module == NULL + && _PyOS_optind < cmdline->args->argc + && wcscmp(cmdline->argv[_PyOS_optind], L"-") != 0) + { + config->run_filename = _PyMem_RawWcsdup(cmdline->argv[_PyOS_optind]); + if (config->run_filename == NULL) { + return _Py_INIT_NO_MEMORY(); + } + } + + if (config->run_command != NULL || config->run_module != NULL) { + /* Backup _PyOS_optind */ + _PyOS_optind--; + } + + /* -c and -m options are exclusive */ + assert(!(config->run_command != NULL && config->run_module != NULL)); + + return _Py_INIT_OK(); +} + + +#ifdef MS_WINDOWS +# define WCSTOK wcstok_s +#else +# define WCSTOK wcstok +#endif + +/* Get warning options from PYTHONWARNINGS environment variable. */ +static _PyInitError +cmdline_init_env_warnoptions(_PyCmdline *cmdline, const _PyCoreConfig *config) +{ + wchar_t *env; + int res = _PyCoreConfig_GetEnvDup(config, &env, + L"PYTHONWARNINGS", "PYTHONWARNINGS"); + if (res < 0) { + return DECODE_LOCALE_ERR("PYTHONWARNINGS", res); + } + + if (env == NULL) { + return _Py_INIT_OK(); + } + + + wchar_t *warning, *context = NULL; + for (warning = WCSTOK(env, L",", &context); + warning != NULL; + warning = WCSTOK(NULL, L",", &context)) + { + _PyInitError err = _Py_wstrlist_append(&cmdline->nenv_warnoption, + &cmdline->env_warnoptions, + warning); + if (_Py_INIT_FAILED(err)) { + PyMem_RawFree(env); + return err; + } + } + PyMem_RawFree(env); + return _Py_INIT_OK(); +} + + +static _PyInitError +config_init_program(_PyCoreConfig *config, const _PyCmdline *cmdline) +{ + wchar_t *program; + if (cmdline->args->argc >= 1 && cmdline->argv != NULL) { + program = cmdline->argv[0]; + } + else { + program = L""; + } + config->program = _PyMem_RawWcsdup(program); + if (config->program == NULL) { + return _Py_INIT_NO_MEMORY(); + } + + return _Py_INIT_OK(); +} + + +static _PyInitError +config_add_warnings_optlist(_PyCoreConfig *config, + int len, wchar_t * const *options) +{ + for (int i = 0; i < len; i++) { + _PyInitError err = _Py_wstrlist_append(&config->nwarnoption, + &config->warnoptions, + options[i]); + if (_Py_INIT_FAILED(err)) { + return err; + } + } + return _Py_INIT_OK(); +} + + +static _PyInitError +config_init_warnoptions(_PyCoreConfig *config, const _PyCmdline *cmdline) +{ + _PyInitError err; + + assert(config->nwarnoption == 0); + + /* The priority order for warnings configuration is (highest precedence + * first): + * + * - the BytesWarning filter, if needed ('-b', '-bb') + * - any '-W' command line options; then + * - the 'PYTHONWARNINGS' environment variable; then + * - the dev mode filter ('-X dev', 'PYTHONDEVMODE'); then + * - any implicit filters added by _warnings.c/warnings.py + * + * All settings except the last are passed to the warnings module via + * the `sys.warnoptions` list. Since the warnings module works on the basis + * of "the most recently added filter will be checked first", we add + * the lowest precedence entries first so that later entries override them. + */ + + if (config->dev_mode) { + err = _Py_wstrlist_append(&config->nwarnoption, + &config->warnoptions, + L"default"); + if (_Py_INIT_FAILED(err)) { + return err; + } + } + + err = config_add_warnings_optlist(config, + cmdline->nenv_warnoption, + cmdline->env_warnoptions); + if (_Py_INIT_FAILED(err)) { + return err; + } + + err = config_add_warnings_optlist(config, + cmdline->nwarnoption, + cmdline->warnoptions); + if (_Py_INIT_FAILED(err)) { + return err; + } + + /* If the bytes_warning_flag isn't set, bytesobject.c and bytearrayobject.c + * don't even try to emit a warning, so we skip setting the filter in that + * case. + */ + if (config->bytes_warning) { + wchar_t *filter; + if (config->bytes_warning> 1) { + filter = L"error::BytesWarning"; + } + else { + filter = L"default::BytesWarning"; + } + err = _Py_wstrlist_append(&config->nwarnoption, + &config->warnoptions, + filter); + if (_Py_INIT_FAILED(err)) { + return err; + } + } + return _Py_INIT_OK(); +} + + +static _PyInitError +config_init_argv(_PyCoreConfig *config, const _PyCmdline *cmdline) +{ + /* Copy argv to be able to modify it (to force -c/-m) */ + int argc = cmdline->args->argc - _PyOS_optind; + wchar_t **argv; + + if (argc <= 0 || cmdline->argv == NULL) { + /* Ensure at least one (empty) argument is seen */ + static wchar_t *empty_argv[1] = {L""}; + argc = 1; + argv = _Py_wstrlist_copy(1, empty_argv); + } + else { + argv = _Py_wstrlist_copy(argc, &cmdline->argv[_PyOS_optind]); + } + + if (argv == NULL) { + return _Py_INIT_NO_MEMORY(); + } + + wchar_t *arg0 = NULL; + if (config->run_command != NULL) { + /* Force sys.argv[0] = '-c' */ + arg0 = L"-c"; + } + else if (config->run_module != NULL) { + /* Force sys.argv[0] = '-m'*/ + arg0 = L"-m"; + } + if (arg0 != NULL) { + arg0 = _PyMem_RawWcsdup(arg0); + if (arg0 == NULL) { + _Py_wstrlist_clear(argc, argv); + return _Py_INIT_NO_MEMORY(); + } + + assert(argc >= 1); + PyMem_RawFree(argv[0]); + argv[0] = arg0; + } + + config->argc = argc; + config->argv = argv; + return _Py_INIT_OK(); +} + + +static void +config_usage(int error, const wchar_t* program) +{ + FILE *f = error ? stderr : stdout; + + fprintf(f, usage_line, program); + if (error) + fprintf(f, "Try `python -h' for more information.\n"); + else { + fputs(usage_1, f); + fputs(usage_2, f); + fputs(usage_3, f); + fprintf(f, usage_4, (wint_t)DELIM); + fprintf(f, usage_5, (wint_t)DELIM, PYTHONHOMEHELP); + fputs(usage_6, f); + } +} + + +/* Parse command line options and environment variables. */ +static _PyInitError +config_from_cmdline(_PyCoreConfig *config, _PyCmdline *cmdline) +{ + int need_usage = 0; + _PyInitError err; + + err = config_init_program(config, cmdline); + if (_Py_INIT_FAILED(err)) { + return err; + } + + err = config_parse_cmdline(config, cmdline, &need_usage); + if (_Py_INIT_FAILED(err)) { + return err; + } + + if (need_usage) { + config_usage(1, config->program); + return _Py_INIT_EXIT(2); + } + + if (cmdline->print_help) { + config_usage(0, config->program); + return _Py_INIT_EXIT(0); + } + + if (cmdline->print_version) { + printf("Python %s\n", + (cmdline->print_version >= 2) ? Py_GetVersion() : PY_VERSION); + return _Py_INIT_EXIT(0); + } + + err = config_init_argv(config, cmdline); + if (_Py_INIT_FAILED(err)) { + return err; + } + + err = _PyCoreConfig_Read(config); + if (_Py_INIT_FAILED(err)) { + return err; + } + + if (config->use_environment) { + err = cmdline_init_env_warnoptions(cmdline, config); + if (_Py_INIT_FAILED(err)) { + return err; + } + } + + err = config_init_warnoptions(config, cmdline); + if (_Py_INIT_FAILED(err)) { + return err; + } + + if (_Py_SetArgcArgv(cmdline->args->argc, cmdline->argv) < 0) { + return _Py_INIT_NO_MEMORY(); + } + return _Py_INIT_OK(); +} + + +static _PyInitError +config_read_from_argv_impl(_PyCoreConfig *config, const _PyArgv *args) +{ + _PyInitError err; + + _PyCmdline cmdline; + memset(&cmdline, 0, sizeof(cmdline)); + cmdline.args = args; + + err = cmdline_decode_argv(&cmdline); + if (_Py_INIT_FAILED(err)) { + goto done; + } + + err = config_from_cmdline(config, &cmdline); + if (_Py_INIT_FAILED(err)) { + goto done; + } + err = _Py_INIT_OK(); + +done: + cmdline_clear(&cmdline); + return err; +} + + +/* Read the configuration into _PyCoreConfig and initialize the LC_CTYPE + locale: enable UTF-8 mode (PEP 540) and/or coerce the C locale (PEP 538). + + Read the configuration from: + + * Command line arguments + * Environment variables + * Py_xxx global configuration variables */ +_PyInitError +_PyCoreConfig_ReadFromArgv(_PyCoreConfig *config, const _PyArgv *args) +{ + _PyInitError err; + int init_utf8_mode = Py_UTF8Mode; +#ifdef MS_WINDOWS + int init_legacy_encoding = Py_LegacyWindowsFSEncodingFlag; +#endif + _PyCoreConfig save_config = _PyCoreConfig_INIT; + int locale_coerced = 0; + int loops = 0; + char *init_ctype_locale = NULL; + + /* copy LC_CTYPE locale */ + const char *loc = setlocale(LC_CTYPE, NULL); + if (loc == NULL) { + err = _Py_INIT_ERR("failed to LC_CTYPE locale"); + goto done; + } + init_ctype_locale = _PyMem_RawStrdup(loc); + if (init_ctype_locale == NULL) { + err = _Py_INIT_NO_MEMORY(); + goto done; + } + + if (_PyCoreConfig_Copy(&save_config, config) < 0) { + err = _Py_INIT_NO_MEMORY(); + goto done; + } + + /* Set LC_CTYPE to the user preferred locale */ + _Py_SetLocaleFromEnv(LC_CTYPE); + + while (1) { + int utf8_mode = config->utf8_mode; + int encoding_changed = 0; + + /* Watchdog to prevent an infinite loop */ + loops++; + if (loops == 3) { + err = _Py_INIT_ERR("Encoding changed twice while " + "reading the configuration"); + goto done; + } + + /* bpo-34207: Py_DecodeLocale() and Py_EncodeLocale() depend + on Py_UTF8Mode and Py_LegacyWindowsFSEncodingFlag. */ + Py_UTF8Mode = config->utf8_mode; +#ifdef MS_WINDOWS + Py_LegacyWindowsFSEncodingFlag = config->legacy_windows_fs_encoding; +#endif + + err = config_read_from_argv_impl(config, args); + if (_Py_INIT_FAILED(err)) { + goto done; + } + if (locale_coerced) { + config->coerce_c_locale = 1; + } + + /* The legacy C locale assumes ASCII as the default text encoding, which + * causes problems not only for the CPython runtime, but also other + * components like GNU readline. + * + * Accordingly, when the CLI detects it, it attempts to coerce it to a + * more capable UTF-8 based alternative. + * + * See the documentation of the PYTHONCOERCECLOCALE setting for more + * details. + */ + if (config->coerce_c_locale && !locale_coerced) { + locale_coerced = 1; + _Py_CoerceLegacyLocale(0); + encoding_changed = 1; + } + + if (utf8_mode == -1) { + if (config->utf8_mode == 1) { + /* UTF-8 Mode enabled */ + encoding_changed = 1; + } + } + else { + if (config->utf8_mode != utf8_mode) { + encoding_changed = 1; + } + } + + if (!encoding_changed) { + break; + } + + /* Reset the configuration before reading again the configuration, + just keep UTF-8 Mode value. */ + int new_utf8_mode = config->utf8_mode; + int new_coerce_c_locale = config->coerce_c_locale; + if (_PyCoreConfig_Copy(config, &save_config) < 0) { + err = _Py_INIT_NO_MEMORY(); + goto done; + } + config->utf8_mode = new_utf8_mode; + config->coerce_c_locale = new_coerce_c_locale; + + /* The encoding changed: read again the configuration + with the new encoding */ + } + err = _Py_INIT_OK(); + +done: + if (init_ctype_locale != NULL) { + setlocale(LC_CTYPE, init_ctype_locale); + } + _PyCoreConfig_Clear(&save_config); + Py_UTF8Mode = init_utf8_mode ; +#ifdef MS_WINDOWS + Py_LegacyWindowsFSEncodingFlag = init_legacy_encoding; +#endif + return err; +}