diff options
author | Victor Stinner <victor.stinner@gmail.com> | 2017-11-15 23:48:08 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-11-15 23:48:08 (GMT) |
commit | f7e5b56c37eb859e225e886c79c5d742c567ee95 (patch) | |
tree | 7d722ca38595aaa68e02a1ee1ea53e17a54b0188 | |
parent | 43605e6bfa8d49612df4a38460d063d6ba781906 (diff) | |
download | cpython-f7e5b56c37eb859e225e886c79c5d742c567ee95.zip cpython-f7e5b56c37eb859e225e886c79c5d742c567ee95.tar.gz cpython-f7e5b56c37eb859e225e886c79c5d742c567ee95.tar.bz2 |
bpo-32030: Split Py_Main() into subfunctions (#4399)
* Don't use "Python runtime" anymore to parse command line options or
to get environment variables: pymain_init() is now a strict
separation.
* Use an error message rather than "crashing" directly with
Py_FatalError(). Limit the number of calls to Py_FatalError(). It
prepares the code to handle errors more nicely later.
* Warnings options (-W, PYTHONWARNINGS) and "XOptions" (-X) are now
only added to the sys module once Python core is properly
initialized.
* _PyMain is now the well identified owner of some important strings
like: warnings options, XOptions, and the "program name". The
program name string is now properly freed at exit.
pymain_free() is now responsible to free the "command" string.
* Rename most methods in Modules/main.c to use a "pymain_" prefix to
avoid conflits and ease debug.
* Replace _Py_CommandLineDetails_INIT with memset(0)
* Reorder a lot of code to fix the initialization ordering. For
example, initializing standard streams now comes before parsing
PYTHONWARNINGS.
* Py_Main() now handles errors when adding warnings options and
XOptions.
* Add _PyMem_GetDefaultRawAllocator() private function.
* Cleanup _PyMem_Initialize(): remove useless global constants: move
them into _PyMem_Initialize().
* Call _PyRuntime_Initialize() as soon as possible:
_PyRuntime_Initialize() now returns an error message on failure.
* Add _PyInitError structure and following macros:
* _Py_INIT_OK()
* _Py_INIT_ERR(msg)
* _Py_INIT_USER_ERR(msg): "user" error, don't abort() in that case
* _Py_INIT_FAILED(err)
-rw-r--r-- | Include/import.h | 2 | ||||
-rw-r--r-- | Include/internal/pystate.h | 8 | ||||
-rw-r--r-- | Include/pylifecycle.h | 44 | ||||
-rw-r--r-- | Include/pymem.h | 4 | ||||
-rw-r--r-- | Include/sysmodule.h | 5 | ||||
-rw-r--r-- | Modules/faulthandler.c | 79 | ||||
-rw-r--r-- | Modules/main.c | 1122 | ||||
-rw-r--r-- | Objects/obmalloc.c | 77 | ||||
-rw-r--r-- | PCbuild/python.vcxproj | 4 | ||||
-rw-r--r-- | Parser/pgenmain.c | 4 | ||||
-rw-r--r-- | Programs/_freeze_importlib.c | 5 | ||||
-rw-r--r-- | Programs/python.c | 14 | ||||
-rw-r--r-- | Python/bootstrap_hash.c | 19 | ||||
-rw-r--r-- | Python/frozenmain.c | 8 | ||||
-rw-r--r-- | Python/import.c | 33 | ||||
-rw-r--r-- | Python/pylifecycle.c | 394 | ||||
-rw-r--r-- | Python/pystate.c | 16 | ||||
-rw-r--r-- | Python/sysmodule.c | 137 |
18 files changed, 1332 insertions, 643 deletions
diff --git a/Include/import.h b/Include/import.h index c30f3ea..26c4b1f 100644 --- a/Include/import.h +++ b/Include/import.h @@ -8,7 +8,7 @@ extern "C" { #endif #ifndef Py_LIMITED_API -PyAPI_FUNC(void) _PyImportZip_Init(void); +PyAPI_FUNC(_PyInitError) _PyImportZip_Init(void); PyMODINIT_FUNC PyInit_imp(void); #endif /* !Py_LIMITED_API */ diff --git a/Include/internal/pystate.h b/Include/internal/pystate.h index 210917b..516f829 100644 --- a/Include/internal/pystate.h +++ b/Include/internal/pystate.h @@ -74,10 +74,16 @@ typedef struct pyruntimestate { // XXX Consolidate globals found via the check-c-globals script. } _PyRuntimeState; +#define _PyRuntimeState_INIT {.initialized = 0, .core_initialized = 0} + PyAPI_DATA(_PyRuntimeState) _PyRuntime; -PyAPI_FUNC(void) _PyRuntimeState_Init(_PyRuntimeState *); +PyAPI_FUNC(_PyInitError) _PyRuntimeState_Init(_PyRuntimeState *); PyAPI_FUNC(void) _PyRuntimeState_Fini(_PyRuntimeState *); +/* Initialize _PyRuntimeState. + Return NULL on success, or return an error message on failure. */ +PyAPI_FUNC(_PyInitError) _PyRuntime_Initialize(void); + #define _Py_CURRENTLY_FINALIZING(tstate) \ (_PyRuntime.finalizing == tstate) diff --git a/Include/pylifecycle.h b/Include/pylifecycle.h index f7286f3..a75b77c 100644 --- a/Include/pylifecycle.h +++ b/Include/pylifecycle.h @@ -20,18 +20,44 @@ PyAPI_FUNC(wchar_t *) Py_GetPythonHome(void); PyAPI_FUNC(int) Py_SetStandardStreamEncoding(const char *encoding, const char *errors); +typedef struct { + const char *prefix; + const char *msg; + int user_err; +} _PyInitError; + +/* Almost all errors causing Python initialization to fail */ +#ifdef _MSC_VER + /* Visual Studio 2015 doesn't implement C99 __func__ in C */ +# define _Py_INIT_GET_FUNC() __FUNCTION__ +#else +# define _Py_INIT_GET_FUNC() __func__ +#endif + +#define _Py_INIT_OK() \ + (_PyInitError){.prefix = NULL, .msg = NULL, .user_err = 0} +#define _Py_INIT_ERR(MSG) \ + (_PyInitError){.prefix = _Py_INIT_GET_FUNC(), .msg = (MSG), .user_err = 0} +/* Error that can be fixed by the user like invalid input parameter. + Don't abort() the process on such error. */ +#define _Py_INIT_USER_ERR(MSG) \ + (_PyInitError){.prefix = _Py_INIT_GET_FUNC(), .msg = (MSG), .user_err = 1} +#define _Py_INIT_FAILED(err) \ + (err.msg != NULL) + /* PEP 432 Multi-phase initialization API (Private while provisional!) */ -PyAPI_FUNC(void) _Py_InitializeCore(const _PyCoreConfig *); +PyAPI_FUNC(_PyInitError) _Py_InitializeCore(const _PyCoreConfig *); PyAPI_FUNC(int) _Py_IsCoreInitialized(void); -PyAPI_FUNC(int) _Py_ReadMainInterpreterConfig(_PyMainInterpreterConfig *); -PyAPI_FUNC(int) _Py_InitializeMainInterpreter(const _PyMainInterpreterConfig *); +PyAPI_FUNC(_PyInitError) _Py_ReadMainInterpreterConfig(_PyMainInterpreterConfig *); +PyAPI_FUNC(_PyInitError) _Py_InitializeMainInterpreter(const _PyMainInterpreterConfig *); #endif /* Initialization and finalization */ PyAPI_FUNC(void) Py_Initialize(void); PyAPI_FUNC(void) Py_InitializeEx(int); #ifndef Py_LIMITED_API -PyAPI_FUNC(void) _Py_InitializeEx_Private(int, int); +PyAPI_FUNC(_PyInitError) _Py_InitializeEx_Private(int, int); +PyAPI_FUNC(void) _Py_FatalInitError(_PyInitError err) _Py_NO_RETURN; #endif PyAPI_FUNC(void) Py_Finalize(void); PyAPI_FUNC(int) Py_FinalizeEx(void); @@ -50,7 +76,7 @@ PyAPI_FUNC(void) _Py_PyAtExit(void (*func)(void)); #endif PyAPI_FUNC(int) Py_AtExit(void (*func)(void)); -PyAPI_FUNC(void) Py_Exit(int); +PyAPI_FUNC(void) Py_Exit(int) _Py_NO_RETURN; /* Restore signals that the interpreter has called SIG_IGN on to SIG_DFL. */ #ifndef Py_LIMITED_API @@ -86,15 +112,15 @@ PyAPI_FUNC(const char *) _Py_gitversion(void); /* Internal -- various one-time initializations */ #ifndef Py_LIMITED_API PyAPI_FUNC(PyObject *) _PyBuiltin_Init(void); -PyAPI_FUNC(PyObject *) _PySys_BeginInit(void); +PyAPI_FUNC(_PyInitError) _PySys_BeginInit(PyObject **sysmod); PyAPI_FUNC(int) _PySys_EndInit(PyObject *sysdict); -PyAPI_FUNC(void) _PyImport_Init(void); +PyAPI_FUNC(_PyInitError) _PyImport_Init(void); PyAPI_FUNC(void) _PyExc_Init(PyObject * bltinmod); -PyAPI_FUNC(void) _PyImportHooks_Init(void); +PyAPI_FUNC(_PyInitError) _PyImportHooks_Init(void); PyAPI_FUNC(int) _PyFrame_Init(void); PyAPI_FUNC(int) _PyFloat_Init(void); PyAPI_FUNC(int) PyByteArray_Init(void); -PyAPI_FUNC(void) _Py_HashRandomization_Init(_PyCoreConfig *core_config); +PyAPI_FUNC(_PyInitError) _Py_HashRandomization_Init(_PyCoreConfig *core_config); #endif /* Various internal finalizers */ diff --git a/Include/pymem.h b/Include/pymem.h index 2670dcd..928851a 100644 --- a/Include/pymem.h +++ b/Include/pymem.h @@ -223,6 +223,10 @@ PyAPI_FUNC(void) PyMem_SetAllocator(PyMemAllocatorDomain domain, PyAPI_FUNC(void) PyMem_SetupDebugHooks(void); #endif +#ifdef Py_BUILD_CORE +PyAPI_FUNC(void) _PyMem_GetDefaultRawAllocator(PyMemAllocatorEx *alloc); +#endif + #ifdef __cplusplus } #endif diff --git a/Include/sysmodule.h b/Include/sysmodule.h index c5547ff..719ecfc 100644 --- a/Include/sysmodule.h +++ b/Include/sysmodule.h @@ -37,6 +37,11 @@ PyAPI_FUNC(PyObject *) PySys_GetXOptions(void); PyAPI_FUNC(size_t) _PySys_GetSizeOf(PyObject *); #endif +#ifdef Py_BUILD_CORE +PyAPI_FUNC(int) _PySys_AddXOptionWithError(const wchar_t *s); +PyAPI_FUNC(int) _PySys_AddWarnOptionWithError(PyObject *option); +#endif + #ifdef __cplusplus } #endif diff --git a/Modules/faulthandler.c b/Modules/faulthandler.c index 33edc05..b6d5aee 100644 --- a/Modules/faulthandler.c +++ b/Modules/faulthandler.c @@ -1281,47 +1281,54 @@ PyInit_faulthandler(void) return m; } -/* Call faulthandler.enable() if the PYTHONFAULTHANDLER environment variable - is defined, or if sys._xoptions has a 'faulthandler' key. */ - static int -faulthandler_env_options(void) +faulthandler_init_enable(void) { - PyObject *xoptions, *key, *module, *res; - char *p; + PyObject *module = PyImport_ImportModule("faulthandler"); + if (module == NULL) { + return -1; + } - if (!((p = Py_GETENV("PYTHONFAULTHANDLER")) && *p != '\0')) { - /* PYTHONFAULTHANDLER environment variable is missing - or an empty string */ - int has_key; + PyObject *res = _PyObject_CallMethodId(module, &PyId_enable, NULL); + Py_DECREF(module); + if (res == NULL) { + return -1; + } + Py_DECREF(res); - xoptions = PySys_GetXOptions(); - if (xoptions == NULL) - return -1; + return 0; +} - key = PyUnicode_FromString("faulthandler"); - if (key == NULL) - return -1; +/* Call faulthandler.enable() if the PYTHONFAULTHANDLER environment variable + is defined, or if sys._xoptions has a 'faulthandler' key. */ - has_key = PyDict_Contains(xoptions, key); - Py_DECREF(key); - if (has_key <= 0) - return has_key; +static int +faulthandler_init_parse(void) +{ + char *p = Py_GETENV("PYTHONFAULTHANDLER"); + if (p && *p != '\0') { + return 1; } - module = PyImport_ImportModule("faulthandler"); - if (module == NULL) { + /* PYTHONFAULTHANDLER environment variable is missing + or an empty string */ + PyObject *xoptions = PySys_GetXOptions(); + if (xoptions == NULL) { return -1; } - res = _PyObject_CallMethodId(module, &PyId_enable, NULL); - Py_DECREF(module); - if (res == NULL) + + PyObject *key = PyUnicode_FromString("faulthandler"); + if (key == NULL) { return -1; - Py_DECREF(res); - return 0; + } + + int has_key = PyDict_Contains(xoptions, key); + Py_DECREF(key); + return has_key; } -int _PyFaulthandler_Init(void) +_PyInitError +_PyFaulthandler_Init(void) { #ifdef HAVE_SIGALTSTACK int err; @@ -1345,14 +1352,22 @@ int _PyFaulthandler_Init(void) thread.cancel_event = PyThread_allocate_lock(); thread.running = PyThread_allocate_lock(); if (!thread.cancel_event || !thread.running) { - PyErr_SetString(PyExc_RuntimeError, - "could not allocate locks for faulthandler"); - return -1; + return _Py_INIT_ERR("failed to allocate locks for faulthandler"); } PyThread_acquire_lock(thread.cancel_event, 1); #endif - return faulthandler_env_options(); + int enable = faulthandler_init_parse(); + if (enable < 0) { + return _Py_INIT_ERR("failed to parse faulthandler env var and cmdline"); + } + + if (enable) { + if (faulthandler_init_enable() < 0) { + return _Py_INIT_ERR("failed to enable faulthandler"); + } + } + return _Py_INIT_OK(); } void _PyFaulthandler_Fini(void) diff --git a/Modules/main.c b/Modules/main.c index 54abbcc..6391ba7 100644 --- a/Modules/main.c +++ b/Modules/main.c @@ -2,6 +2,7 @@ #include "Python.h" #include "osdefs.h" +#include "internal/pystate.h" #include <locale.h> @@ -37,7 +38,7 @@ extern "C" { /* For Py_GetArgcArgv(); set by main() */ static wchar_t **orig_argv; -static int orig_argc; +static int orig_argc; /* command line options */ #define BASE_OPTS L"bBc:dEhiIJm:OqRsStuvVW:xX:?" @@ -109,13 +110,13 @@ static const char usage_6[] = " coercion behavior. Use PYTHONCOERCECLOCALE=warn to request display of\n" " locale coercion and locale compatibility warnings on stderr.\n"; -static int -usage(int exitcode, const wchar_t* program) +static void +usage(int error, const wchar_t* program) { - FILE *f = exitcode ? stderr : stdout; + FILE *f = error ? stderr : stdout; fprintf(f, usage_line, program); - if (exitcode) + if (error) fprintf(f, "Try `python -h' for more information.\n"); else { fputs(usage_1, f); @@ -125,50 +126,57 @@ usage(int exitcode, const wchar_t* program) fprintf(f, usage_5, (wint_t)DELIM, PYTHONHOMEHELP); fputs(usage_6, f); } - return exitcode; } -static void RunStartupFile(PyCompilerFlags *cf) +static void +pymain_run_statup(PyCompilerFlags *cf) { char *startup = Py_GETENV("PYTHONSTARTUP"); - if (startup != NULL && startup[0] != '\0') { - FILE *fp = _Py_fopen(startup, "r"); - if (fp != NULL) { - (void) PyRun_SimpleFileExFlags(fp, startup, 0, cf); - PyErr_Clear(); - fclose(fp); - } else { - int save_errno; - - save_errno = errno; - PySys_WriteStderr("Could not open PYTHONSTARTUP\n"); - errno = save_errno; - PyErr_SetFromErrnoWithFilename(PyExc_OSError, - startup); - PyErr_Print(); - PyErr_Clear(); - } + if (startup == NULL || startup[0] == '\0') { + 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(); + PyErr_Clear(); + return; } + + (void) PyRun_SimpleFileExFlags(fp, startup, 0, cf); + PyErr_Clear(); + fclose(fp); } -static void RunInteractiveHook(void) +static void +pymain_run_interactive_hook(void) { PyObject *sys, *hook, *result; sys = PyImport_ImportModule("sys"); - if (sys == NULL) + if (sys == NULL) { goto error; + } + hook = PyObject_GetAttrString(sys, "__interactivehook__"); Py_DECREF(sys); - if (hook == NULL) + if (hook == NULL) { PyErr_Clear(); - else { - result = _PyObject_CallNoArg(hook); - Py_DECREF(hook); - if (result == NULL) - goto error; - else - Py_DECREF(result); + return; + } + + result = _PyObject_CallNoArg(hook); + Py_DECREF(hook); + if (result == NULL) { + goto error; } + Py_DECREF(result); + return; error: @@ -178,7 +186,8 @@ error: } -static int RunModule(wchar_t *modname, int set_argv0) +static int +pymain_run_module(wchar_t *modname, int set_argv0) { PyObject *module, *runpy, *runmodule, *runargs, *result; runpy = PyImport_ImportModule("runpy"); @@ -228,23 +237,26 @@ static int RunModule(wchar_t *modname, int set_argv0) } static PyObject * -AsImportPathEntry(wchar_t *filename) +pymain_get_importer(wchar_t *filename) { PyObject *sys_path0 = NULL, *importer; sys_path0 = PyUnicode_FromWideChar(filename, wcslen(filename)); - if (sys_path0 == NULL) + if (sys_path0 == NULL) { goto error; + } importer = PyImport_GetImporter(sys_path0); - if (importer == NULL) + if (importer == NULL) { goto error; + } if (importer == Py_None) { Py_DECREF(sys_path0); Py_DECREF(importer); return NULL; } + Py_DECREF(importer); return sys_path0; @@ -258,49 +270,25 @@ error: static int -RunMainFromImporter(PyObject *sys_path0) -{ - PyObject *sys_path; - int sts; - - /* Assume sys_path0 has already been checked by AsImportPathEntry, - * so put it in sys.path[0] and import __main__ */ - sys_path = PySys_GetObject("path"); - if (sys_path == NULL) { - PyErr_SetString(PyExc_RuntimeError, "unable to get sys.path"); - goto error; - } - sts = PyList_Insert(sys_path, 0, sys_path0); - if (sts) { - sys_path0 = NULL; - goto error; - } - - sts = RunModule(L"__main__", 0); - return sts != 0; - -error: - Py_XDECREF(sys_path0); - PyErr_Print(); - return 1; -} - -static int -run_command(wchar_t *command, PyCompilerFlags *cf) +pymain_run_command(wchar_t *command, PyCompilerFlags *cf) { PyObject *unicode, *bytes; int ret; unicode = PyUnicode_FromWideChar(command, -1); - if (unicode == NULL) + if (unicode == NULL) { goto error; + } + bytes = PyUnicode_AsUTF8String(unicode); Py_DECREF(unicode); - if (bytes == NULL) + if (bytes == NULL) { goto error; + } + ret = PyRun_SimpleStringFlags(PyBytes_AsString(bytes), cf); Py_DECREF(bytes); - return ret != 0; + return (ret != 0); error: PySys_WriteStderr("Unable to decode the command from the command line:\n"); @@ -308,8 +296,9 @@ error: return 1; } + static int -run_file(FILE *fp, const wchar_t *filename, PyCompilerFlags *p_cf) +pymain_run_file(FILE *fp, const wchar_t *filename, PyCompilerFlags *p_cf) { PyObject *unicode, *bytes = NULL; const char *filename_str; @@ -327,15 +316,17 @@ run_file(FILE *fp, const wchar_t *filename, PyCompilerFlags *p_cf) bytes = PyUnicode_EncodeFSDefault(unicode); Py_DECREF(unicode); } - if (bytes != NULL) + if (bytes != NULL) { filename_str = PyBytes_AsString(bytes); + } else { PyErr_Clear(); filename_str = "<encoding error>"; } } - else + else { filename_str = "<stdin>"; + } run = PyRun_AnyFileExFlags(fp, filename_str, filename != NULL, p_cf); Py_XDECREF(bytes); @@ -348,10 +339,15 @@ run_file(FILE *fp, const wchar_t *filename, PyCompilerFlags *p_cf) /*TODO: Add arg processing to PEP 432 as a new configuration setup API */ typedef struct { + size_t len; + wchar_t **options; +} _Py_OptList; + +typedef struct { wchar_t *filename; /* Trailing arg without -c or -m */ wchar_t *command; /* -c argument */ wchar_t *module; /* -m argument */ - PyObject *warning_options; /* -W options */ + _Py_OptList warning_options; /* -W options */ PyObject *extra_options; /* -X options */ int print_help; /* -h, -? options */ int print_version; /* -V option */ @@ -368,36 +364,189 @@ typedef struct { int verbosity; /* Py_VerboseFlag */ int quiet_flag; /* Py_QuietFlag */ int skip_first_line; /* -x option */ + _Py_OptList xoptions; /* -X options */ } _Py_CommandLineDetails; -#define _Py_CommandLineDetails_INIT \ - {NULL, NULL, NULL, NULL, NULL, \ - 0, 0, 0, 0, 0, 0, 0, 0, \ - 0, 0, 0, 0, 0, 0, 0} +/* Structure used by Py_Main() to pass data to subfunctions */ +typedef struct { + /* Exit status ("exit code") */ + int status; + PyCompilerFlags cf; + /* non-zero is stdin is a TTY or if -i option is used */ + int stdin_is_interactive; + _PyCoreConfig core_config; + _Py_CommandLineDetails cmdline; + PyObject *main_importer_path; + /* non-zero if filename, command (-c) or module (-m) is set + on the command line */ + int run_code; + wchar_t *program_name; + /* Error message if a function failed */ + _PyInitError err; + /* PYTHONWARNINGS env var */ + _Py_OptList env_warning_options; + int argc; + wchar_t **argv; +} _PyMain; + +/* .cmdline is initialized to zeros */ +#define _PyMain_INIT \ + {.status = 0, \ + .cf = {.cf_flags = 0}, \ + .core_config = _PyCoreConfig_INIT, \ + .main_importer_path = NULL, \ + .run_code = -1, \ + .program_name = NULL, \ + .err = _Py_INIT_OK(), \ + .env_warning_options = {0, NULL}} + + +#define INIT_NO_MEMORY() _Py_INIT_ERR("memory allocation failed") + + +static void +pymain_optlist_clear(_Py_OptList *list) +{ + for (size_t i=0; i < list->len; i++) { + PyMem_RawFree(list->options[i]); + } + PyMem_RawFree(list->options); + list->len = 0; + list->options = NULL; +} + +static void +pymain_free_impl(_PyMain *pymain) +{ + _Py_CommandLineDetails *cmdline = &pymain->cmdline; + pymain_optlist_clear(&cmdline->warning_options); + pymain_optlist_clear(&cmdline->xoptions); + PyMem_RawFree(cmdline->command); + + pymain_optlist_clear(&pymain->env_warning_options); + Py_CLEAR(pymain->main_importer_path); + PyMem_RawFree(pymain->program_name); + +#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 void +pymain_free(_PyMain *pymain) +{ + /* Call pymain_free() with the memory allocator used by pymain_init() */ + PyMemAllocatorEx old_alloc, raw_alloc; + PyMem_GetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); + _PyMem_GetDefaultRawAllocator(&raw_alloc); + PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &raw_alloc); + + pymain_free_impl(pymain); + + PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); +} + + +static int +pymain_run_main_from_importer(_PyMain *pymain) +{ + PyObject *sys_path0 = pymain->main_importer_path; + PyObject *sys_path; + int sts; + + /* Assume sys_path0 has already been checked by pymain_get_importer(), + * so put it in sys.path[0] and import __main__ */ + sys_path = PySys_GetObject("path"); + if (sys_path == NULL) { + PyErr_SetString(PyExc_RuntimeError, "unable to get sys.path"); + goto error; + } + + sts = PyList_Insert(sys_path, 0, sys_path0); + if (sts) { + sys_path0 = NULL; + goto error; + } + + sts = pymain_run_module(L"__main__", 0); + return sts != 0; + +error: + Py_CLEAR(pymain->main_importer_path); + PyErr_Print(); + return 1; +} + + +static wchar_t* +pymain_strdup(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) { + return NULL; + } + memcpy(str2, str, len * sizeof(wchar_t)); + return str2; +} + + +static int +pymain_optlist_append(_Py_OptList *list, wchar_t *str) +{ + wchar_t *str2 = pymain_strdup(str); + if (str2 == NULL) { + return -1; + } + + size_t size = (list->len + 1) * sizeof(list[0]); + wchar_t **options2 = (wchar_t **)PyMem_RawRealloc(list->options, size); + if (options2 == NULL) { + PyMem_RawFree(str2); + return -1; + } + options2[list->len] = str2; + list->options = options2; + list->len++; + return 0; +} + +/* Parse the command line arguments + Return 0 on success. + Return 1 if parsing failed. + Set pymain->err and return -1 on other errors. */ static int -read_command_line(int argc, wchar_t **argv, _Py_CommandLineDetails *cmdline) +pymain_parse_cmdline(_PyMain *pymain) { - PyObject *warning_option = NULL; - wchar_t *command = NULL; - wchar_t *module = NULL; - int c; + _Py_CommandLineDetails *cmdline = &pymain->cmdline; _PyOS_ResetGetOpt(); + do { + int c = _PyOS_GetOpt(pymain->argc, pymain->argv, PROGRAM_OPTS); + if (c == EOF) { + break; + } - while ((c = _PyOS_GetOpt(argc, argv, PROGRAM_OPTS)) != EOF) { if (c == 'c') { - size_t len; /* -c is the last option; following arguments that look like options are left for the command to interpret. */ - - len = wcslen(_PyOS_optarg) + 1 + 1; - command = (wchar_t *)PyMem_RawMalloc(sizeof(wchar_t) * len); - if (command == NULL) - Py_FatalError( - "not enough memory to copy -c argument"); - wcscpy(command, _PyOS_optarg); + size_t len = wcslen(_PyOS_optarg) + 1 + 1; + wchar_t *command = PyMem_RawMalloc(sizeof(wchar_t) * len); + if (command == NULL) { + goto out_of_memory; + } + memcpy(command, _PyOS_optarg, len * sizeof(wchar_t)); command[len - 2] = '\n'; command[len - 1] = 0; cmdline->command = command; @@ -408,8 +557,7 @@ read_command_line(int argc, wchar_t **argv, _Py_CommandLineDetails *cmdline) /* -m is the last option; following arguments that look like options are left for the module to interpret. */ - module = _PyOS_optarg; - cmdline->module = module; + cmdline->module = _PyOS_optarg; break; } @@ -428,6 +576,7 @@ read_command_line(int argc, wchar_t **argv, _Py_CommandLineDetails *cmdline) break; case 'I': + pymain->core_config.ignore_environment++; cmdline->isolated++; cmdline->no_user_site_directory++; break; @@ -451,7 +600,7 @@ read_command_line(int argc, wchar_t **argv, _Py_CommandLineDetails *cmdline) break; case 'E': - /* Handled prior to core initialization */ + pymain->core_config.ignore_environment++; break; case 't': @@ -480,21 +629,17 @@ read_command_line(int argc, wchar_t **argv, _Py_CommandLineDetails *cmdline) break; case 'W': - if (cmdline->warning_options == NULL) - cmdline->warning_options = PyList_New(0); - if (cmdline->warning_options == NULL) - Py_FatalError("failure in handling of -W argument"); - warning_option = PyUnicode_FromWideChar(_PyOS_optarg, -1); - if (warning_option == NULL) - Py_FatalError("failure in handling of -W argument"); - if (PyList_Append(cmdline->warning_options, warning_option) == -1) - Py_FatalError("failure in handling of -W argument"); - Py_DECREF(warning_option); + if (pymain_optlist_append(&cmdline->warning_options, + _PyOS_optarg) < 0) { + goto out_of_memory; + } break; case 'X': - /* TODO: Delay addition of X options to sys module */ - PySys_AddXOption(_PyOS_optarg); + if (pymain_optlist_append(&cmdline->xoptions, + _PyOS_optarg) < 0) { + goto out_of_memory; + } break; case 'q': @@ -508,20 +653,26 @@ read_command_line(int argc, wchar_t **argv, _Py_CommandLineDetails *cmdline) /* This space reserved for other options */ default: - return -1; - /*NOTREACHED*/ - + /* unknown argument: parsing failed */ + return 1; } - } + } while (1); - if (command == NULL && module == NULL && _PyOS_optind < argc && - wcscmp(argv[_PyOS_optind], L"-") != 0) + if (cmdline->command == NULL && cmdline->module == NULL + && _PyOS_optind < pymain->argc + && wcscmp(pymain->argv[_PyOS_optind], L"-") != 0) { - cmdline->filename = argv[_PyOS_optind]; + cmdline->filename = pymain->argv[_PyOS_optind]; } + return 0; + +out_of_memory: + pymain->err = INIT_NO_MEMORY(); + return -1; } + static void maybe_set_flag(int *flag, int value) { @@ -534,146 +685,142 @@ maybe_set_flag(int *flag, int value) } } + static int -apply_command_line_and_environment(_Py_CommandLineDetails *cmdline) +pymain_add_xoptions(_PyMain *pymain) { - maybe_set_flag(&Py_BytesWarningFlag, cmdline->bytes_warning); - maybe_set_flag(&Py_DebugFlag, cmdline->debug); - maybe_set_flag(&Py_InspectFlag, cmdline->inspect); - maybe_set_flag(&Py_InteractiveFlag, cmdline->interactive); - maybe_set_flag(&Py_IsolatedFlag, cmdline->isolated); - maybe_set_flag(&Py_OptimizeFlag, cmdline->optimization_level); - maybe_set_flag(&Py_DontWriteBytecodeFlag, cmdline->dont_write_bytecode); - maybe_set_flag(&Py_NoUserSiteDirectory, cmdline->no_user_site_directory); - maybe_set_flag(&Py_NoSiteFlag, cmdline->no_site_import); - maybe_set_flag(&Py_UnbufferedStdioFlag, cmdline->use_unbuffered_io); - maybe_set_flag(&Py_VerboseFlag, cmdline->verbosity); - maybe_set_flag(&Py_QuietFlag, cmdline->quiet_flag); - - /* TODO: Apply PYTHONWARNINGS & -W options to sys module here */ - /* TODO: Apply -X options to sys module here */ + _Py_OptList *options = &pymain->cmdline.xoptions; + for (size_t i=0; i < options->len; i++) { + wchar_t *option = options->options[i]; + if (_PySys_AddXOptionWithError(option) < 0) { + pymain->err = INIT_NO_MEMORY(); + return -1; + } + } return 0; } -int -Py_Main(int argc, wchar_t **argv) -{ - int c; - int sts; - FILE *fp = stdin; - char *p; -#ifdef MS_WINDOWS - wchar_t *wp; -#endif - int stdin_is_interactive = 0; - _Py_CommandLineDetails cmdline = _Py_CommandLineDetails_INIT; - _PyCoreConfig core_config = _PyCoreConfig_INIT; - PyCompilerFlags cf; - PyObject *main_importer_path = NULL; - - cf.cf_flags = 0; - orig_argc = argc; /* For Py_GetArgcArgv() */ - orig_argv = argv; - - /* Hash randomization needed early for all string operations - (including -W and -X options). */ - _PyOS_opterr = 0; /* prevent printing the error in 1st pass */ - while ((c = _PyOS_GetOpt(argc, argv, PROGRAM_OPTS)) != EOF) { - if (c == 'm' || c == 'c') { - /* -c / -m is the last option: following arguments are - not interpreter options. */ - break; +static int +pymain_add_warnings_optlist(_Py_OptList *warnings) +{ + for (size_t i = 0; i < warnings->len; i++) { + PyObject *option = PyUnicode_FromWideChar(warnings->options[i], -1); + if (option == NULL) { + return -1; } - if (c == 'E' || c == 'I') { - core_config.ignore_environment++; - break; + if (_PySys_AddWarnOptionWithError(option)) { + Py_DECREF(option); + return -1; } + Py_DECREF(option); } + return 0; +} - /* Initialize the core language runtime */ - Py_IgnoreEnvironmentFlag = core_config.ignore_environment; - core_config._disable_importlib = 0; - core_config.allocator = Py_GETENV("PYTHONMALLOC"); - _Py_InitializeCore(&core_config); +static int +pymain_add_warnings_options(_PyMain *pymain) +{ + PySys_ResetWarnOptions(); - /* Reprocess the command line with the language runtime available */ - if (read_command_line(argc, argv, &cmdline)) { - return usage(2, argv[0]); + if (pymain_add_warnings_optlist(&pymain->env_warning_options) < 0) { + pymain->err = INIT_NO_MEMORY(); + return -1; } - - if (cmdline.print_help) { - return usage(0, argv[0]); + if (pymain_add_warnings_optlist(&pymain->cmdline.warning_options) < 0) { + pymain->err = INIT_NO_MEMORY(); + return -1; } + return 0; +} - if (cmdline.print_version) { - printf("Python %s\n", cmdline.print_version >= 2 ? Py_GetVersion() : PY_VERSION); + +/* Get warning options from PYTHONWARNINGS environment variable. + Return 0 on success. + Set pymain->err and return -1 on error. */ +static int +pymain_warnings_envvar(_PyMain *pymain) +{ + if (Py_IgnoreEnvironmentFlag) { return 0; } - PySys_ResetWarnOptions(); - apply_command_line_and_environment(&cmdline); - #ifdef MS_WINDOWS - if (!Py_IgnoreEnvironmentFlag && (wp = _wgetenv(L"PYTHONWARNINGS")) && - *wp != L'\0') { + wchar_t *wp; + + if ((wp = _wgetenv(L"PYTHONWARNINGS")) && *wp != L'\0') { wchar_t *buf, *warning, *context = NULL; buf = (wchar_t *)PyMem_RawMalloc((wcslen(wp) + 1) * sizeof(wchar_t)); - if (buf == NULL) - Py_FatalError( - "not enough memory to copy PYTHONWARNINGS"); + if (buf == NULL) { + goto out_of_memory; + } wcscpy(buf, wp); for (warning = wcstok_s(buf, L",", &context); warning != NULL; warning = wcstok_s(NULL, L",", &context)) { - PySys_AddWarnOption(warning); + + if (pymain_optlist_append(&pymain->env_warning_options, + warning) < 0) { + PyMem_RawFree(buf); + goto out_of_memory; + } } PyMem_RawFree(buf); } #else + char *p; + if ((p = Py_GETENV("PYTHONWARNINGS")) && *p != '\0') { char *buf, *oldloc; - PyObject *unicode; /* settle for strtok here as there's no one standard C89 wcstok */ buf = (char *)PyMem_RawMalloc(strlen(p) + 1); - if (buf == NULL) - Py_FatalError( - "not enough memory to copy PYTHONWARNINGS"); + if (buf == NULL) { + goto out_of_memory; + } strcpy(buf, p); oldloc = _PyMem_RawStrdup(setlocale(LC_ALL, NULL)); setlocale(LC_ALL, ""); for (p = strtok(buf, ","); p != NULL; p = strtok(NULL, ",")) { -#ifdef __APPLE__ - /* Use utf-8 on Mac OS X */ - unicode = PyUnicode_FromString(p); -#else - unicode = PyUnicode_DecodeLocale(p, "surrogateescape"); -#endif - if (unicode == NULL) { - /* ignore errors */ - PyErr_Clear(); - continue; + size_t len; + wchar_t *warning = Py_DecodeLocale(p, &len); + if (warning == NULL) { + if (len == (size_t)-2) { + pymain->err = _Py_INIT_ERR("failed to decode " + "PYTHONWARNINGS"); + return -1; + } + else { + goto out_of_memory; + } } - PySys_AddWarnOptionUnicode(unicode); - Py_DECREF(unicode); + if (pymain_optlist_append(&pymain->env_warning_options, + warning) < 0) { + PyMem_RawFree(warning); + goto out_of_memory; + } + PyMem_RawFree(warning); } setlocale(LC_ALL, oldloc); PyMem_RawFree(oldloc); PyMem_RawFree(buf); } #endif - if (cmdline.warning_options != NULL) { - Py_ssize_t i; - for (i = 0; i < PyList_GET_SIZE(cmdline.warning_options); i++) { - PySys_AddWarnOptionUnicode(PyList_GET_ITEM(cmdline.warning_options, i)); - } - Py_DECREF(cmdline.warning_options); - } + return 0; - stdin_is_interactive = Py_FdIsInteractive(stdin, (char *)0); +out_of_memory: + pymain->err = INIT_NO_MEMORY(); + return -1; +} + + +static void +pymain_init_stdio(_PyMain *pymain) +{ + pymain->stdin_is_interactive = (isatty(fileno(stdin)) + || Py_InteractiveFlag); #if defined(MS_WINDOWS) || defined(__CYGWIN__) /* don't translate newlines (\r\n <=> \n) */ @@ -706,8 +853,20 @@ Py_Main(int argc, wchar_t **argv) #endif /* !MS_WINDOWS */ /* Leave stderr alone - it should be unbuffered anyway. */ } +} + + +/* Get the program name: use PYTHONEXECUTABLE and __PYVENV_LAUNCHER__ + environment variables on macOS if available, use argv[0] by default. + Return 0 on success. + Set pymain->err and return -1 on error. */ +static int +pymain_get_program_name(_PyMain *pymain) +{ + assert(pymain->program_name == NULL); #ifdef __APPLE__ + char *p; /* On MacOS X, when the Python interpreter is embedded in an application bundle, it gets executed by a bootstrapping script that does os.execve() with an argv[0] that's different from the @@ -723,171 +882,295 @@ Py_Main(int argc, wchar_t **argv) buffer = PyMem_RawMalloc(len * sizeof(wchar_t)); if (buffer == NULL) { - Py_FatalError( - "not enough memory to copy PYTHONEXECUTABLE"); + goto out_of_memory; } mbstowcs(buffer, p, len); - Py_SetProgramName(buffer); - /* buffer is now handed off - do not free */ - } else { + pymain->program_name = buffer; + } #ifdef WITH_NEXT_FRAMEWORK + else { char* pyvenv_launcher = getenv("__PYVENV_LAUNCHER__"); - if (pyvenv_launcher && *pyvenv_launcher) { /* Used by Mac/Tools/pythonw.c to forward * the argv0 of the stub executable */ - wchar_t* wbuf = Py_DecodeLocale(pyvenv_launcher, NULL); - + size_t len; + wchar_t* wbuf = Py_DecodeLocale(pyvenv_launcher, &len); if (wbuf == NULL) { - Py_FatalError("Cannot decode __PYVENV_LAUNCHER__"); + if (len == (size_t)-2) { + pymain->err = _Py_INIT_ERR("failed to decode " + "__PYVENV_LAUNCHER__"); + return -1; + } + else { + goto out_of_memory; + } } - Py_SetProgramName(wbuf); - - /* Don't free wbuf, the argument to Py_SetProgramName - * must remain valid until Py_FinalizeEx is called. - */ - } else { - Py_SetProgramName(argv[0]); + pymain->program_name = wbuf; } -#else - Py_SetProgramName(argv[0]); -#endif } -#else - Py_SetProgramName(argv[0]); -#endif - /* Replaces previous call to Py_Initialize() - * - * TODO: Move environment queries (etc) into Py_ReadConfig - */ - { - _PyMainInterpreterConfig config = _PyMainInterpreterConfig_INIT; +#endif /* WITH_NEXT_FRAMEWORK */ +#endif /* __APPLE__ */ + + if (pymain->program_name == NULL) { + /* Use argv[0] by default */ + pymain->program_name = pymain_strdup(pymain->argv[0]); + if (pymain->program_name == NULL) { + goto out_of_memory; + } + } + return 0; + +out_of_memory: + pymain->err = INIT_NO_MEMORY(); + return -1; +} - /* TODO: Moar config options! */ - config.install_signal_handlers = 1; - /* TODO: Print any exceptions raised by these operations */ - if (_Py_ReadMainInterpreterConfig(&config)) - Py_FatalError("Py_Main: Py_ReadMainInterpreterConfig failed"); - if (_Py_InitializeMainInterpreter(&config)) - Py_FatalError("Py_Main: Py_InitializeMainInterpreter failed"); + +/* Initialize the main interpreter. + * + * Replaces previous call to Py_Initialize() + * + * TODO: Move environment queries (etc) into Py_ReadConfig + * + * Return 0 on success. + * Set pymain->err and return -1 on error. + */ +static int +pymain_init_main_interpreter(_PyMain *pymain) +{ + _PyMainInterpreterConfig config = _PyMainInterpreterConfig_INIT; + _PyInitError err; + + /* TODO: Moar config options! */ + config.install_signal_handlers = 1; + + /* TODO: Print any exceptions raised by these operations */ + err = _Py_ReadMainInterpreterConfig(&config); + if (_Py_INIT_FAILED(err)) { + pymain->err = err; + return -1; } + err = _Py_InitializeMainInterpreter(&config); + if (_Py_INIT_FAILED(err)) { + pymain->err = err; + return -1; + } + return 0; +} + + +static void +pymain_header(_PyMain *pymain) +{ /* TODO: Move this to _PyRun_PrepareMain */ - if (!Py_QuietFlag && (Py_VerboseFlag || - (cmdline.command == NULL && cmdline.filename == NULL && - cmdline.module == NULL && stdin_is_interactive))) { - fprintf(stderr, "Python %s on %s\n", - Py_GetVersion(), Py_GetPlatform()); - if (!Py_NoSiteFlag) - fprintf(stderr, "%s\n", COPYRIGHT); + if (Py_QuietFlag) { + return; + } + + if (!Py_VerboseFlag && (pymain->run_code || !pymain->stdin_is_interactive)) { + return; } + fprintf(stderr, "Python %s on %s\n", Py_GetVersion(), Py_GetPlatform()); + if (!Py_NoSiteFlag) { + fprintf(stderr, "%s\n", COPYRIGHT); + } +} + + +static void +pymain_init_argv(_PyMain *pymain) +{ + _Py_CommandLineDetails *cmdline = &pymain->cmdline; + /* TODO: Move this to _Py_InitializeMainInterpreter */ - if (cmdline.command != NULL) { + if (cmdline->command != NULL) { /* Backup _PyOS_optind and force sys.argv[0] = '-c' */ _PyOS_optind--; - argv[_PyOS_optind] = L"-c"; + pymain->argv[_PyOS_optind] = L"-c"; } - if (cmdline.module != NULL) { + if (cmdline->module != NULL) { /* Backup _PyOS_optind and force sys.argv[0] = '-m'*/ _PyOS_optind--; - argv[_PyOS_optind] = L"-m"; + pymain->argv[_PyOS_optind] = L"-m"; } - if (cmdline.filename != NULL) { - main_importer_path = AsImportPathEntry(cmdline.filename); + if (cmdline->filename != NULL) { + pymain->main_importer_path = pymain_get_importer(cmdline->filename); } - if (main_importer_path != NULL) { - /* Let RunMainFromImporter adjust sys.path[0] later */ - PySys_SetArgvEx(argc-_PyOS_optind, argv+_PyOS_optind, 0); + int update_path; + if (pymain->main_importer_path != NULL) { + /* Let pymain_run_main_from_importer() adjust sys.path[0] later */ + update_path = 0; } else { /* Use config settings to decide whether or not to update sys.path[0] */ - PySys_SetArgv(argc-_PyOS_optind, argv+_PyOS_optind); + update_path = (Py_IsolatedFlag == 0); } + PySys_SetArgvEx(pymain->argc - _PyOS_optind, + pymain->argv + _PyOS_optind, + update_path); +} - if ((Py_InspectFlag || (cmdline.command == NULL && - cmdline.filename == NULL && - cmdline.module == NULL)) && - isatty(fileno(stdin)) && - !Py_IsolatedFlag) { - PyObject *v; - v = PyImport_ImportModule("readline"); - if (v == NULL) - PyErr_Clear(); - else - Py_DECREF(v); + +/* Set Py_XXX global configuration variables */ +static void +pymain_set_global_config(_PyMain *pymain) +{ + _Py_CommandLineDetails *cmdline = &pymain->cmdline; + maybe_set_flag(&Py_BytesWarningFlag, cmdline->bytes_warning); + maybe_set_flag(&Py_DebugFlag, cmdline->debug); + maybe_set_flag(&Py_InspectFlag, cmdline->inspect); + maybe_set_flag(&Py_InteractiveFlag, cmdline->interactive); + maybe_set_flag(&Py_IsolatedFlag, cmdline->isolated); + maybe_set_flag(&Py_OptimizeFlag, cmdline->optimization_level); + maybe_set_flag(&Py_DontWriteBytecodeFlag, cmdline->dont_write_bytecode); + maybe_set_flag(&Py_NoUserSiteDirectory, cmdline->no_user_site_directory); + maybe_set_flag(&Py_NoSiteFlag, cmdline->no_site_import); + maybe_set_flag(&Py_UnbufferedStdioFlag, cmdline->use_unbuffered_io); + maybe_set_flag(&Py_VerboseFlag, cmdline->verbosity); + maybe_set_flag(&Py_QuietFlag, cmdline->quiet_flag); + + maybe_set_flag(&Py_IgnoreEnvironmentFlag, pymain->core_config.ignore_environment); +} + + +/* 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) +{ + if (Py_IsolatedFlag) { + return; + } + if (!Py_InspectFlag && pymain->run_code) { + return; + } + if (!isatty(fileno(stdin))) { + return; } - if (cmdline.command) { - sts = run_command(cmdline.command, &cf); - PyMem_RawFree(cmdline.command); - } else if (cmdline.module) { - sts = (RunModule(cmdline.module, 1) != 0); + PyObject *mod = PyImport_ImportModule("readline"); + if (mod == NULL) { + PyErr_Clear(); } else { + Py_DECREF(mod); + } +} - if (cmdline.filename == NULL && stdin_is_interactive) { - Py_InspectFlag = 0; /* do exit on SystemExit */ - RunStartupFile(&cf); - RunInteractiveHook(); - } - /* XXX */ - - sts = -1; /* keep track of whether we've already run __main__ */ - if (main_importer_path != NULL) { - sts = RunMainFromImporter(main_importer_path); - } +static FILE* +pymain_open_filename(_PyMain *pymain) +{ + _Py_CommandLineDetails *cmdline = &pymain->cmdline; + FILE* fp; + + fp = _Py_wfopen(cmdline->filename, L"r"); + if (fp == NULL) { + char *cfilename_buffer; + const char *cfilename; + int err = errno; + cfilename_buffer = Py_EncodeLocale(cmdline->filename, NULL); + if (cfilename_buffer != NULL) + cfilename = cfilename_buffer; + else + cfilename = "<unprintable file name>"; + fprintf(stderr, "%ls: can't open file '%s': [Errno %d] %s\n", + pymain->argv[0], cfilename, err, strerror(err)); + PyMem_Free(cfilename_buffer); + pymain->status = 2; + return NULL; + } - if (sts==-1 && cmdline.filename != NULL) { - fp = _Py_wfopen(cmdline.filename, L"r"); - if (fp == NULL) { - char *cfilename_buffer; - const char *cfilename; - int err = errno; - cfilename_buffer = Py_EncodeLocale(cmdline.filename, NULL); - if (cfilename_buffer != NULL) - cfilename = cfilename_buffer; - else - cfilename = "<unprintable file name>"; - fprintf(stderr, "%ls: can't open file '%s': [Errno %d] %s\n", - argv[0], cfilename, err, strerror(err)); - if (cfilename_buffer) - PyMem_Free(cfilename_buffer); - return 2; - } - else if (cmdline.skip_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", - argv[0], cmdline.filename); - fclose(fp); - return 1; - } + if (cmdline->skip_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", + pymain->argv[0], cmdline->filename); + fclose(fp); + pymain->status = 1; + return NULL; + } + + return fp; +} - if (sts == -1) - sts = run_file(fp, cmdline.filename, &cf); + +static void +pymain_run(_PyMain *pymain) +{ + _Py_CommandLineDetails *cmdline = &pymain->cmdline; + + if (cmdline->filename == NULL && pymain->stdin_is_interactive) { + Py_InspectFlag = 0; /* do exit on SystemExit */ + pymain_run_statup(&pymain->cf); + pymain_run_interactive_hook(); + } + /* XXX */ + + if (pymain->main_importer_path != NULL) { + pymain->status = pymain_run_main_from_importer(pymain); + return; + } + + FILE *fp; + if (cmdline->filename != NULL) { + fp = pymain_open_filename(pymain); + if (fp == NULL) { + return; + } + } + else { + fp = stdin; } + pymain->status = pymain_run_file(fp, cmdline->filename, &pymain->cf); +} + + +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. */ @@ -897,34 +1180,185 @@ Py_Main(int argc, wchar_t **argv) Py_InspectFlag = 1; } - if (Py_InspectFlag && stdin_is_interactive && - (cmdline.filename != NULL || cmdline.command != NULL || cmdline.module != NULL)) { + if (Py_InspectFlag && pymain->stdin_is_interactive && pymain->run_code) { Py_InspectFlag = 0; - RunInteractiveHook(); + pymain_run_interactive_hook(); /* XXX */ - sts = PyRun_AnyFileFlags(stdin, "<stdin>", &cf) != 0; + int res = PyRun_AnyFileFlags(stdin, "<stdin>", &pymain->cf); + pymain->status = (res != 0); + } +} + + +/* Parse the command line. + Handle --version and --help options directly. + + Return 1 if Python must exit. + Return 0 on success. + Set pymain->err and return -1 on failure. */ +static int +pymain_init_cmdline(_PyMain *pymain) +{ + _Py_CommandLineDetails *cmdline = &pymain->cmdline; + + int res = pymain_parse_cmdline(pymain); + if (res < 0) { + return -1; + } + if (res) { + usage(1, pymain->argv[0]); + pymain->status = 2; + return 1; + } + + if (cmdline->print_help) { + usage(0, pymain->argv[0]); + pymain->status = 0; + return 1; + } + + if (cmdline->print_version) { + printf("Python %s\n", + (cmdline->print_version >= 2) ? Py_GetVersion() : PY_VERSION); + return 1; + } + + pymain->run_code = (cmdline->command != NULL || cmdline->filename != NULL + || cmdline->module != NULL); + + return 0; +} + + +/* Initialize Py_Main(). + 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_impl(_PyMain *pymain) +{ + _PyCoreConfig *core_config = &pymain->core_config; + core_config->_disable_importlib = 0; + + orig_argc = pymain->argc; /* For Py_GetArgcArgv() */ + orig_argv = pymain->argv; + + /* Parse the command line */ + int res = pymain_init_cmdline(pymain); + if (res < 0) { + return -1; + } + if (res > 0) { + return 1; + } + + pymain_set_global_config(pymain); + pymain_init_stdio(pymain); + + /* Get environment variables */ + if (pymain_warnings_envvar(pymain) < 0) { + return -1; + } + if (pymain_get_program_name(pymain) < 0) { + return -1; } + core_config->allocator = Py_GETENV("PYTHONMALLOC"); + + return 0; +} + + +static int +pymain_init(_PyMain *pymain) +{ + pymain->err = _PyRuntime_Initialize(); + if (_Py_INIT_FAILED(pymain->err)) { + return -1; + } + + /* Make sure that all memory allocated in pymain_init() is allocated + by malloc() */ + 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); + + /* Restore the old memory allocator */ + PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); + + return res; +} + +static int +pymain_core(_PyMain *pymain) +{ + _Py_CommandLineDetails *cmdline = &pymain->cmdline; + + pymain->err = _Py_InitializeCore(&pymain->core_config); + if (_Py_INIT_FAILED(pymain->err)) { + return -1; + } + + if (pymain_configure_pyruntime(pymain)) { + return -1; + } + + if (pymain_init_main_interpreter(pymain)) { + return -1; + } + + pymain_header(pymain); + pymain_import_readline(pymain); + + pymain_init_argv(pymain); + + if (cmdline->command) { + pymain->status = pymain_run_command(cmdline->command, &pymain->cf); + } + else if (cmdline->module) { + pymain->status = (pymain_run_module(cmdline->module, 1) != 0); + } + else { + pymain_run(pymain); + } + pymain_repl(pymain); if (Py_FinalizeEx() < 0) { /* Value unlikely to be confused with a non-error exit status or other special meaning */ - sts = 120; + pymain->status = 120; } -#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__ */ + return 0; +} + + +int +Py_Main(int argc, wchar_t **argv) +{ + _PyMain pymain = _PyMain_INIT; + memset(&pymain.cmdline, 0, sizeof(pymain.cmdline)); + pymain.argc = argc; + pymain.argv = argv; + + int res = pymain_init(&pymain); + if (res < 0) { + _Py_FatalInitError(pymain.err); + } + if (res == 0) { + res = pymain_core(&pymain); + if (res < 0) { + _Py_FatalInitError(pymain.err); + } + } + + pymain_free(&pymain); - return sts; + return pymain.status; } /* this is gonna seem *real weird*, but if you put some other code between diff --git a/Objects/obmalloc.c b/Objects/obmalloc.c index 4441c82..699cce9 100644 --- a/Objects/obmalloc.c +++ b/Objects/obmalloc.c @@ -182,31 +182,17 @@ static struct { #define _PyMem_Raw _PyRuntime.mem.allocators.raw -static const PyMemAllocatorEx _pymem_raw = { -#ifdef Py_DEBUG - &_PyMem_Debug.raw, PYRAWDBG_FUNCS -#else - NULL, PYRAW_FUNCS -#endif - }; #define _PyMem _PyRuntime.mem.allocators.mem -static const PyMemAllocatorEx _pymem = { -#ifdef Py_DEBUG - &_PyMem_Debug.mem, PYDBG_FUNCS -#else - NULL, PYMEM_FUNCS -#endif - }; #define _PyObject _PyRuntime.mem.allocators.obj -static const PyMemAllocatorEx _pyobject = { -#ifdef Py_DEBUG - &_PyMem_Debug.obj, PYDBG_FUNCS -#else - NULL, PYOBJ_FUNCS -#endif - }; + +void +_PyMem_GetDefaultRawAllocator(PyMemAllocatorEx *alloc_p) +{ + PyMemAllocatorEx alloc = {NULL, PYRAW_FUNCS}; + *alloc_p = alloc; +} int _PyMem_SetupAllocators(const char *opt) @@ -267,34 +253,52 @@ _PyMem_SetupAllocators(const char *opt) return 0; } -#undef PYRAW_FUNCS -#undef PYMEM_FUNCS -#undef PYOBJ_FUNCS -#undef PYRAWDBG_FUNCS -#undef PYDBG_FUNCS -static const PyObjectArenaAllocator _PyObject_Arena = {NULL, +void +_PyObject_Initialize(struct _pyobj_runtime_state *state) +{ + PyObjectArenaAllocator _PyObject_Arena = {NULL, #ifdef MS_WINDOWS - _PyObject_ArenaVirtualAlloc, _PyObject_ArenaVirtualFree + _PyObject_ArenaVirtualAlloc, _PyObject_ArenaVirtualFree #elif defined(ARENAS_USE_MMAP) - _PyObject_ArenaMmap, _PyObject_ArenaMunmap + _PyObject_ArenaMmap, _PyObject_ArenaMunmap #else - _PyObject_ArenaMalloc, _PyObject_ArenaFree + _PyObject_ArenaMalloc, _PyObject_ArenaFree #endif }; -void -_PyObject_Initialize(struct _pyobj_runtime_state *state) -{ state->allocator_arenas = _PyObject_Arena; } + void _PyMem_Initialize(struct _pymem_runtime_state *state) { - state->allocators.raw = _pymem_raw; - state->allocators.mem = _pymem; - state->allocators.obj = _pyobject; + PyMemAllocatorEx pymem_raw = { +#ifdef Py_DEBUG + &_PyMem_Debug.raw, PYRAWDBG_FUNCS +#else + NULL, PYRAW_FUNCS +#endif + }; + PyMemAllocatorEx pymem = { +#ifdef Py_DEBUG + &_PyMem_Debug.mem, PYDBG_FUNCS +#else + NULL, PYMEM_FUNCS +#endif + }; + PyMemAllocatorEx pyobject = { +#ifdef Py_DEBUG + &_PyMem_Debug.obj, PYDBG_FUNCS +#else + NULL, PYOBJ_FUNCS +#endif + }; + + state->allocators.raw = pymem_raw; + state->allocators.mem = pymem; + state->allocators.obj = pyobject; #ifdef WITH_PYMALLOC Py_BUILD_ASSERT(NB_SMALL_SIZE_CLASSES == 64); @@ -311,6 +315,7 @@ _PyMem_Initialize(struct _pymem_runtime_state *state) #endif /* WITH_PYMALLOC */ } + #ifdef WITH_PYMALLOC static int _PyMem_DebugEnabled(void) diff --git a/PCbuild/python.vcxproj b/PCbuild/python.vcxproj index ab9fb05..423e68c 100644 --- a/PCbuild/python.vcxproj +++ b/PCbuild/python.vcxproj @@ -57,7 +57,7 @@ </PropertyGroup> <ItemDefinitionGroup> <ClCompile> - <PreprocessorDefinitions>_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>Py_BUILD_CORE;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> <Link> <SubSystem>Console</SubSystem> @@ -113,4 +113,4 @@ $(_PGOPath) </PropertyGroup> <WriteLinesToFile File="$(PySourcePath)python.bat" Lines="$(_Content)" Overwrite="true" Condition="'$(_Content)' != '$(_ExistingContent)'" /> </Target> -</Project>
\ No newline at end of file +</Project> diff --git a/Parser/pgenmain.c b/Parser/pgenmain.c index 8f3e2f7..dbdf134 100644 --- a/Parser/pgenmain.c +++ b/Parser/pgenmain.c @@ -27,13 +27,11 @@ int Py_DebugFlag; int Py_VerboseFlag; int Py_IgnoreEnvironmentFlag; -_PyRuntimeState _PyRuntime = {0, 0}; +_PyRuntimeState _PyRuntime = _PyRuntimeState_INIT; /* Forward */ grammar *getgrammar(const char *filename); -void Py_Exit(int) _Py_NO_RETURN; - void Py_Exit(int sts) { diff --git a/Programs/_freeze_importlib.c b/Programs/_freeze_importlib.c index 1069966..b8b630c 100644 --- a/Programs/_freeze_importlib.c +++ b/Programs/_freeze_importlib.c @@ -81,7 +81,10 @@ main(int argc, char *argv[]) Py_SetProgramName(L"./_freeze_importlib"); /* Don't install importlib, since it could execute outdated bytecode. */ - _Py_InitializeEx_Private(1, 0); + _PyInitError err = _Py_InitializeEx_Private(1, 0); + if (_Py_INIT_FAILED(err)) { + _Py_FatalInitError(err); + } if (strstr(inpath, "_external") != NULL) { is_bootstrap = 0; diff --git a/Programs/python.c b/Programs/python.c index 270a11b..707e38f 100644 --- a/Programs/python.c +++ b/Programs/python.c @@ -1,6 +1,7 @@ /* Minimal main program -- everything is loaded from the library */ #include "Python.h" +#include "internal/pystate.h" #include <locale.h> #ifdef __FreeBSD__ @@ -22,9 +23,16 @@ main(int argc, char **argv) wchar_t **argv_copy; /* We need a second copy, as Python might modify the first one. */ wchar_t **argv_copy2; - int i, res; + int i, status; char *oldloc; + _PyInitError err = _PyRuntime_Initialize(); + if (_Py_INIT_FAILED(err)) { + fprintf(stderr, "Fatal Python error: %s\n", err.msg); + fflush(stderr); + exit(1); + } + /* Force malloc() allocator to bootstrap Python */ #ifdef Py_DEBUG (void)_PyMem_SetupAllocators("malloc_debug"); @@ -88,7 +96,7 @@ main(int argc, char **argv) setlocale(LC_ALL, oldloc); PyMem_RawFree(oldloc); - res = Py_Main(argc, argv_copy); + status = Py_Main(argc, argv_copy); /* Force again malloc() allocator to release memory blocks allocated before Py_Main() */ @@ -103,6 +111,6 @@ main(int argc, char **argv) } PyMem_RawFree(argv_copy); PyMem_RawFree(argv_copy2); - return res; + return status; } #endif diff --git a/Python/bootstrap_hash.c b/Python/bootstrap_hash.c index 807d602..e667fa9 100644 --- a/Python/bootstrap_hash.c +++ b/Python/bootstrap_hash.c @@ -561,15 +561,16 @@ int Py_ReadHashSeed(char *seed_text, return 0; } -static void +static _PyInitError init_hash_secret(int use_hash_seed, unsigned long hash_seed) { void *secret = &_Py_HashSecret; Py_ssize_t secret_size = sizeof(_Py_HashSecret_t); - if (_Py_HashSecret_Initialized) - return; + if (_Py_HashSecret_Initialized) { + return _Py_INIT_OK(); + } _Py_HashSecret_Initialized = 1; if (use_hash_seed) { @@ -593,12 +594,14 @@ init_hash_secret(int use_hash_seed, pyurandom() is non-blocking mode (blocking=0): see the PEP 524. */ res = pyurandom(secret, secret_size, 0, 0); if (res < 0) { - Py_FatalError("failed to get random numbers to initialize Python"); + return _Py_INIT_ERR("failed to get random numbers " + "to initialize Python"); } } + return _Py_INIT_OK(); } -void +_PyInitError _Py_HashRandomization_Init(_PyCoreConfig *core_config) { char *seed_text; @@ -608,13 +611,13 @@ _Py_HashRandomization_Init(_PyCoreConfig *core_config) if (use_hash_seed < 0) { seed_text = Py_GETENV("PYTHONHASHSEED"); if (Py_ReadHashSeed(seed_text, &use_hash_seed, &hash_seed) < 0) { - Py_FatalError("PYTHONHASHSEED must be \"random\" or an integer " - "in range [0; 4294967295]"); + return _Py_INIT_USER_ERR("PYTHONHASHSEED must be \"random\" " + "or an integer in range [0; 4294967295]"); } core_config->use_hash_seed = use_hash_seed; core_config->hash_seed = hash_seed; } - init_hash_secret(use_hash_seed, hash_seed); + return init_hash_secret(use_hash_seed, hash_seed); } void diff --git a/Python/frozenmain.c b/Python/frozenmain.c index 769b33d..77602d7 100644 --- a/Python/frozenmain.c +++ b/Python/frozenmain.c @@ -2,6 +2,7 @@ /* Python interpreter main program for frozen scripts */ #include "Python.h" +#include "internal/pystate.h" #include <locale.h> #ifdef MS_WINDOWS @@ -15,6 +16,13 @@ extern int PyInitFrozenExtensions(void); int Py_FrozenMain(int argc, char **argv) { + _PyInitError err = _PyRuntime_Initialize(); + if (_Py_INIT_FAILED(err)) { + fprintf(stderr, "Fatal Python error: %s\n", err.msg); + fflush(stderr); + exit(1); + } + char *p; int i, n, sts = 1; int inspect = 0; diff --git a/Python/import.c b/Python/import.c index 950c872..fe60844 100644 --- a/Python/import.c +++ b/Python/import.c @@ -42,19 +42,23 @@ module _imp /* Initialize things */ -void +_PyInitError _PyImport_Init(void) { PyInterpreterState *interp = PyThreadState_Get()->interp; initstr = PyUnicode_InternFromString("__init__"); - if (initstr == NULL) - Py_FatalError("Can't initialize import variables"); + if (initstr == NULL) { + return _Py_INIT_ERR("Can't initialize import variables"); + } + interp->builtins_copy = PyDict_Copy(interp->builtins); - if (interp->builtins_copy == NULL) - Py_FatalError("Can't backup builtins dict"); + if (interp->builtins_copy == NULL) { + return _Py_INIT_ERR("Can't backup builtins dict"); + } + return _Py_INIT_OK(); } -void +_PyInitError _PyImportHooks_Init(void) { PyObject *v, *path_hooks = NULL; @@ -80,15 +84,18 @@ _PyImportHooks_Init(void) goto error; err = PySys_SetObject("path_hooks", path_hooks); if (err) { - error: - PyErr_Print(); - Py_FatalError("initializing sys.meta_path, sys.path_hooks, " - "or path_importer_cache failed"); + goto error; } Py_DECREF(path_hooks); + return _Py_INIT_OK(); + + error: + PyErr_Print(); + return _Py_INIT_ERR("initializing sys.meta_path, sys.path_hooks, " + "or path_importer_cache failed"); } -void +_PyInitError _PyImportZip_Init(void) { PyObject *path_hooks, *zimpimport; @@ -133,11 +140,11 @@ _PyImportZip_Init(void) } } - return; + return _Py_INIT_OK(); error: PyErr_Print(); - Py_FatalError("initializing zipimport failed"); + return _Py_INIT_ERR("initializing zipimport failed"); } /* Locking primitives to prevent parallel imports of the same module diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 8817be1..66cc711 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -53,11 +53,11 @@ extern wchar_t *Py_GetPath(void); extern grammar _PyParser_Grammar; /* From graminit.c */ /* Forward */ -static void initmain(PyInterpreterState *interp); -static int initfsencoding(PyInterpreterState *interp); -static void initsite(void); +static _PyInitError add_main_module(PyInterpreterState *interp); +static _PyInitError initfsencoding(PyInterpreterState *interp); +static _PyInitError initsite(void); static int initstdio(void); -static void initsigs(void); +static _PyInitError initsigs(void); static void call_py_exitfuncs(void); static void wait_for_thread_shutdown(void); static void call_ll_exitfuncs(void); @@ -66,7 +66,7 @@ extern int _PyStructSequence_Init(void); extern void _PyUnicode_Fini(void); extern int _PyLong_Init(void); extern void PyLong_Fini(void); -extern int _PyFaulthandler_Init(void); +extern _PyInitError _PyFaulthandler_Init(void); extern void _PyFaulthandler_Fini(void); extern void _PyHash_Fini(void); extern int _PyTraceMalloc_Init(void); @@ -76,9 +76,9 @@ extern void _Py_ReadyTypes(void); extern void _PyGILState_Init(PyInterpreterState *, PyThreadState *); extern void _PyGILState_Fini(void); -_PyRuntimeState _PyRuntime = {0, 0}; +_PyRuntimeState _PyRuntime = _PyRuntimeState_INIT; -void +_PyInitError _PyRuntime_Initialize(void) { /* XXX We only initialize once in the process, which aligns with @@ -88,10 +88,12 @@ _PyRuntime_Initialize(void) This is because the runtime state is not properly finalized currently. */ static int initialized = 0; - if (initialized) - return; + if (initialized) { + return _Py_INIT_OK(); + } initialized = 1; - _PyRuntimeState_Init(&_PyRuntime); + + return _PyRuntimeState_Init(&_PyRuntime); } void @@ -282,43 +284,44 @@ get_locale_encoding(void) #endif } -static void +/* Return NULL on success, or return an error message on failure */ +static _PyInitError initimport(PyInterpreterState *interp, PyObject *sysmod) { PyObject *importlib; PyObject *impmod; PyObject *value; + _PyInitError err; /* Import _importlib through its frozen version, _frozen_importlib. */ if (PyImport_ImportFrozenModule("_frozen_importlib") <= 0) { - Py_FatalError("Py_Initialize: can't import _frozen_importlib"); + return _Py_INIT_ERR("can't import _frozen_importlib"); } else if (Py_VerboseFlag) { PySys_FormatStderr("import _frozen_importlib # frozen\n"); } importlib = PyImport_AddModule("_frozen_importlib"); if (importlib == NULL) { - Py_FatalError("Py_Initialize: couldn't get _frozen_importlib from " - "sys.modules"); + return _Py_INIT_ERR("couldn't get _frozen_importlib from sys.modules"); } interp->importlib = importlib; Py_INCREF(interp->importlib); interp->import_func = PyDict_GetItemString(interp->builtins, "__import__"); if (interp->import_func == NULL) - Py_FatalError("Py_Initialize: __import__ not found"); + return _Py_INIT_ERR("__import__ not found"); Py_INCREF(interp->import_func); /* Import the _imp module */ impmod = PyInit_imp(); if (impmod == NULL) { - Py_FatalError("Py_Initialize: can't import _imp"); + return _Py_INIT_ERR("can't import _imp"); } else if (Py_VerboseFlag) { PySys_FormatStderr("import _imp # builtin\n"); } if (_PyImport_SetModuleString("_imp", impmod) < 0) { - Py_FatalError("Py_Initialize: can't save _imp to sys.modules"); + return _Py_INIT_ERR("can't save _imp to sys.modules"); } /* Install importlib as the implementation of import */ @@ -330,15 +333,21 @@ initimport(PyInterpreterState *interp, PyObject *sysmod) } if (value == NULL) { PyErr_Print(); - Py_FatalError("Py_Initialize: importlib install failed"); + return _Py_INIT_ERR("importlib install failed"); } Py_DECREF(value); Py_DECREF(impmod); - _PyImportZip_Init(); + err = _PyImportZip_Init(); + if (_Py_INIT_FAILED(err)) { + return err; + } + + return _Py_INIT_OK(); } -static void +/* Return NULL on success, or return an error message on failure */ +static _PyInitError initexternalimport(PyInterpreterState *interp) { PyObject *value; @@ -346,9 +355,10 @@ initexternalimport(PyInterpreterState *interp) "_install_external_importers", ""); if (value == NULL) { PyErr_Print(); - Py_FatalError("Py_EndInitialization: external importer setup failed"); + return _Py_INIT_ERR("external importer setup failed"); } Py_DECREF(value); + return _Py_INIT_OK(); } /* Helper functions to better handle the legacy C locale @@ -613,13 +623,16 @@ _Py_SetLocaleFromEnv(int category) * Any code invoked from this function should *not* assume it has access * to the Python C API (unless the API is explicitly listed as being * safe to call without calling Py_Initialize first) + * + * Return NULL on success, or return an error message on failure. */ /* TODO: Progressively move functionality from Py_BeginInitialization to * Py_ReadConfig and Py_EndInitialization */ -void _Py_InitializeCore(const _PyCoreConfig *config) +_PyInitError +_Py_InitializeCore(const _PyCoreConfig *config) { PyInterpreterState *interp; PyThreadState *tstate; @@ -627,18 +640,26 @@ void _Py_InitializeCore(const _PyCoreConfig *config) char *p; _PyCoreConfig core_config = _PyCoreConfig_INIT; _PyMainInterpreterConfig preinit_config = _PyMainInterpreterConfig_INIT; + _PyInitError err; - _PyRuntime_Initialize(); + err = _PyRuntime_Initialize(); + if (_Py_INIT_FAILED(err)) { + return err; + } if (config != NULL) { core_config = *config; } + if (_PyMem_SetupAllocators(core_config.allocator) < 0) { + return _Py_INIT_ERR("Unknown PYTHONMALLOC allocator"); + } + if (_PyRuntime.initialized) { - Py_FatalError("Py_InitializeCore: main interpreter already initialized"); + return _Py_INIT_ERR("main interpreter already initialized"); } if (_PyRuntime.core_initialized) { - Py_FatalError("Py_InitializeCore: runtime core already initialized"); + return _Py_INIT_ERR("runtime core already initialized"); } /* Py_Finalize leaves _Py_Finalizing set in order to help daemon @@ -652,13 +673,6 @@ void _Py_InitializeCore(const _PyCoreConfig *config) */ _PyRuntime.finalizing = NULL; - if (_PyMem_SetupAllocators(core_config.allocator) < 0) { - fprintf(stderr, - "Error in PYTHONMALLOC: unknown allocator \"%s\"!\n", - core_config.allocator); - exit(1); - } - #ifndef MS_WINDOWS /* Set up the LC_CTYPE locale, so we can obtain the locale's charset without having to switch @@ -692,7 +706,11 @@ void _Py_InitializeCore(const _PyCoreConfig *config) set_flag(&Py_LegacyWindowsStdioFlag, p); #endif - _Py_HashRandomization_Init(&core_config); + err = _Py_HashRandomization_Init(&core_config); + if (_Py_INIT_FAILED(err)) { + return err; + } + if (!core_config.use_hash_seed || core_config.hash_seed) { /* Random or non-zero hash seed */ Py_HashRandomizationFlag = 1; @@ -701,13 +719,13 @@ void _Py_InitializeCore(const _PyCoreConfig *config) _PyInterpreterState_Enable(&_PyRuntime); interp = PyInterpreterState_New(); if (interp == NULL) - Py_FatalError("Py_InitializeCore: can't make main interpreter"); + return _Py_INIT_ERR("can't make main interpreter"); interp->core_config = core_config; interp->config = preinit_config; tstate = PyThreadState_New(interp); if (tstate == NULL) - Py_FatalError("Py_InitializeCore: can't make first thread"); + return _Py_INIT_ERR("can't make first thread"); (void) PyThreadState_Swap(tstate); /* We can't call _PyEval_FiniThreads() in Py_FinalizeEx because @@ -722,46 +740,50 @@ void _Py_InitializeCore(const _PyCoreConfig *config) _Py_ReadyTypes(); if (!_PyFrame_Init()) - Py_FatalError("Py_InitializeCore: can't init frames"); + return _Py_INIT_ERR("can't init frames"); if (!_PyLong_Init()) - Py_FatalError("Py_InitializeCore: can't init longs"); + return _Py_INIT_ERR("can't init longs"); if (!PyByteArray_Init()) - Py_FatalError("Py_InitializeCore: can't init bytearray"); + return _Py_INIT_ERR("can't init bytearray"); if (!_PyFloat_Init()) - Py_FatalError("Py_InitializeCore: can't init float"); + return _Py_INIT_ERR("can't init float"); PyObject *modules = PyDict_New(); if (modules == NULL) - Py_FatalError("Py_InitializeCore: can't make modules dictionary"); + return _Py_INIT_ERR("can't make modules dictionary"); interp->modules = modules; - sysmod = _PySys_BeginInit(); - if (sysmod == NULL) - Py_FatalError("Py_InitializeCore: can't initialize sys"); + err = _PySys_BeginInit(&sysmod); + if (_Py_INIT_FAILED(err)) { + return err; + } + interp->sysdict = PyModule_GetDict(sysmod); - if (interp->sysdict == NULL) - Py_FatalError("Py_InitializeCore: can't initialize sys dict"); + if (interp->sysdict == NULL) { + return _Py_INIT_ERR("can't initialize sys dict"); + } + Py_INCREF(interp->sysdict); PyDict_SetItemString(interp->sysdict, "modules", modules); _PyImport_FixupBuiltin(sysmod, "sys", modules); /* Init Unicode implementation; relies on the codec registry */ if (_PyUnicode_Init() < 0) - Py_FatalError("Py_InitializeCore: can't initialize unicode"); + return _Py_INIT_ERR("can't initialize unicode"); if (_PyStructSequence_Init() < 0) - Py_FatalError("Py_InitializeCore: can't initialize structseq"); + return _Py_INIT_ERR("can't initialize structseq"); bimod = _PyBuiltin_Init(); if (bimod == NULL) - Py_FatalError("Py_InitializeCore: can't initialize builtins modules"); + return _Py_INIT_ERR("can't initialize builtins modules"); _PyImport_FixupBuiltin(bimod, "builtins", modules); interp->builtins = PyModule_GetDict(bimod); if (interp->builtins == NULL) - Py_FatalError("Py_InitializeCore: can't initialize builtins dict"); + return _Py_INIT_ERR("can't initialize builtins dict"); Py_INCREF(interp->builtins); /* initialize builtin exceptions */ @@ -771,25 +793,35 @@ void _Py_InitializeCore(const _PyCoreConfig *config) infrastructure for the io module in place. */ pstderr = PyFile_NewStdPrinter(fileno(stderr)); if (pstderr == NULL) - Py_FatalError("Py_InitializeCore: can't set preliminary stderr"); + return _Py_INIT_ERR("can't set preliminary stderr"); _PySys_SetObjectId(&PyId_stderr, pstderr); PySys_SetObject("__stderr__", pstderr); Py_DECREF(pstderr); - _PyImport_Init(); + err = _PyImport_Init(); + if (_Py_INIT_FAILED(err)) { + return err; + } - _PyImportHooks_Init(); + err = _PyImportHooks_Init(); + if (_Py_INIT_FAILED(err)) { + return err; + } /* Initialize _warnings. */ _PyWarnings_Init(); /* This call sets up builtin and frozen import support */ if (!interp->core_config._disable_importlib) { - initimport(interp, sysmod); + err = initimport(interp, sysmod); + if (_Py_INIT_FAILED(err)) { + return err; + } } /* Only when we get here is the runtime core fully initialized */ _PyRuntime.core_initialized = 1; + return _Py_INIT_OK(); } /* Read configuration settings from standard locations @@ -802,16 +834,18 @@ void _Py_InitializeCore(const _PyCoreConfig *config) * * More advanced selective initialization tricks are possible by calling * this function multiple times with various preconfigured settings. + * + * Return NULL on success, or return an error message on failure. */ -int _Py_ReadMainInterpreterConfig(_PyMainInterpreterConfig *config) +_PyInitError +_Py_ReadMainInterpreterConfig(_PyMainInterpreterConfig *config) { /* Signal handlers are installed by default */ if (config->install_signal_handlers < 0) { config->install_signal_handlers = 1; } - - return 0; + return _Py_INIT_OK(); } /* Update interpreter state based on supplied configuration settings @@ -824,26 +858,30 @@ int _Py_ReadMainInterpreterConfig(_PyMainInterpreterConfig *config) * initialized or without a valid current thread state is a fatal error. * Other errors should be reported as normal Python exceptions with a * non-zero return code. + * + * Return NULL on success, or return an error message on failure. */ -int _Py_InitializeMainInterpreter(const _PyMainInterpreterConfig *config) +_PyInitError +_Py_InitializeMainInterpreter(const _PyMainInterpreterConfig *config) { PyInterpreterState *interp; PyThreadState *tstate; + _PyInitError err; if (!_PyRuntime.core_initialized) { - Py_FatalError("Py_InitializeMainInterpreter: runtime core not initialized"); + return _Py_INIT_ERR("runtime core not initialized"); } if (_PyRuntime.initialized) { - Py_FatalError("Py_InitializeMainInterpreter: main interpreter already initialized"); + return _Py_INIT_ERR("main interpreter already initialized"); } /* Get current thread state and interpreter pointer */ tstate = PyThreadState_GET(); if (!tstate) - Py_FatalError("Py_InitializeMainInterpreter: failed to read thread state"); + return _Py_INIT_ERR("failed to read thread state"); interp = tstate->interp; if (!interp) - Py_FatalError("Py_InitializeMainInterpreter: failed to get interpreter"); + return _Py_INIT_ERR("failed to get interpreter"); /* Now finish configuring the main interpreter */ interp->config = *config; @@ -855,12 +893,12 @@ int _Py_InitializeMainInterpreter(const _PyMainInterpreterConfig *config) * or pure Python code in the standard library won't work. */ _PyRuntime.initialized = 1; - return 0; + return _Py_INIT_OK(); } /* TODO: Report exceptions rather than fatal errors below here */ if (_PyTime_Init() < 0) - Py_FatalError("Py_InitializeMainInterpreter: can't initialize time"); + return _Py_INIT_ERR("can't initialize time"); /* Finish setting up the sys module and import system */ /* GetPath may initialize state that _PySys_EndInit locks @@ -868,26 +906,40 @@ int _Py_InitializeMainInterpreter(const _PyMainInterpreterConfig *config) /* TODO: Call Py_GetPath() in Py_ReadConfig, rather than here */ PySys_SetPath(Py_GetPath()); if (_PySys_EndInit(interp->sysdict) < 0) - Py_FatalError("Py_InitializeMainInterpreter: can't finish initializing sys"); - initexternalimport(interp); + return _Py_INIT_ERR("can't finish initializing sys"); + err = initexternalimport(interp); + if (_Py_INIT_FAILED(err)) { + return err; + } /* initialize the faulthandler module */ - if (_PyFaulthandler_Init()) - Py_FatalError("Py_InitializeMainInterpreter: can't initialize faulthandler"); + err = _PyFaulthandler_Init(); + if (_Py_INIT_FAILED(err)) { + return err; + } - if (initfsencoding(interp) < 0) - Py_FatalError("Py_InitializeMainInterpreter: unable to load the file system codec"); + err = initfsencoding(interp); + if (_Py_INIT_FAILED(err)) { + return err; + } - if (config->install_signal_handlers) - initsigs(); /* Signal handling stuff, including initintr() */ + if (config->install_signal_handlers) { + err = initsigs(); /* Signal handling stuff, including initintr() */ + if (_Py_INIT_FAILED(err)) { + return err; + } + } if (_PyTraceMalloc_Init() < 0) - Py_FatalError("Py_InitializeMainInterpreter: can't initialize tracemalloc"); + return _Py_INIT_ERR("can't initialize tracemalloc"); - initmain(interp); /* Module __main__ */ - if (initstdio() < 0) - Py_FatalError( - "Py_InitializeMainInterpreter: can't initialize sys standard streams"); + err = add_main_module(interp); + if (_Py_INIT_FAILED(err)) { + return err; + } + if (initstdio() < 0) { + return _Py_INIT_ERR("can't initialize sys standard streams"); + } /* Initialize warnings. */ if (PySys_HasWarnOptions()) { @@ -901,37 +953,57 @@ int _Py_InitializeMainInterpreter(const _PyMainInterpreterConfig *config) _PyRuntime.initialized = 1; - if (!Py_NoSiteFlag) - initsite(); /* Module site */ + if (!Py_NoSiteFlag) { + err = initsite(); /* Module site */ + if (_Py_INIT_FAILED(err)) { + return err; + } + } - return 0; + return _Py_INIT_OK(); } #undef _INIT_DEBUG_PRINT -void +_PyInitError _Py_InitializeEx_Private(int install_sigs, int install_importlib) { _PyCoreConfig core_config = _PyCoreConfig_INIT; _PyMainInterpreterConfig config = _PyMainInterpreterConfig_INIT; + _PyInitError err; /* TODO: Moar config options! */ core_config.ignore_environment = Py_IgnoreEnvironmentFlag; core_config._disable_importlib = !install_importlib; config.install_signal_handlers = install_sigs; - _Py_InitializeCore(&core_config); + + err = _Py_InitializeCore(&core_config); + if (_Py_INIT_FAILED(err)) { + return err; + } + /* TODO: Print any exceptions raised by these operations */ - if (_Py_ReadMainInterpreterConfig(&config)) - Py_FatalError("Py_Initialize: Py_ReadMainInterpreterConfig failed"); - if (_Py_InitializeMainInterpreter(&config)) - Py_FatalError("Py_Initialize: Py_InitializeMainInterpreter failed"); + err = _Py_ReadMainInterpreterConfig(&config); + if (_Py_INIT_FAILED(err)) { + return err; + } + + err = _Py_InitializeMainInterpreter(&config); + if (_Py_INIT_FAILED(err)) { + return err; + } + + return _Py_INIT_OK(); } void Py_InitializeEx(int install_sigs) { - _Py_InitializeEx_Private(install_sigs, 1); + _PyInitError err = _Py_InitializeEx_Private(install_sigs, 1); + if (_Py_INIT_FAILED(err)) { + _Py_FatalInitError(err); + } } void @@ -1236,6 +1308,7 @@ Py_NewInterpreter(void) PyInterpreterState *interp; PyThreadState *tstate, *save_tstate; PyObject *bimod, *sysmod; + _PyInitError err = _Py_INIT_OK(); if (!_PyRuntime.initialized) Py_FatalError("Py_NewInterpreter: call Py_Initialize first"); @@ -1308,25 +1381,50 @@ Py_NewInterpreter(void) PySys_SetObject("__stderr__", pstderr); Py_DECREF(pstderr); - _PyImportHooks_Init(); + err = _PyImportHooks_Init(); + if (_Py_INIT_FAILED(err)) { + goto init_failed; + } - initimport(interp, sysmod); - initexternalimport(interp); + err = initimport(interp, sysmod); + if (_Py_INIT_FAILED(err)) { + goto init_failed; + } - if (initfsencoding(interp) < 0) - goto handle_error; + err = initexternalimport(interp); + if (_Py_INIT_FAILED(err)) { + goto init_failed; + } - if (initstdio() < 0) - Py_FatalError( - "Py_NewInterpreter: can't initialize sys standard streams"); - initmain(interp); - if (!Py_NoSiteFlag) - initsite(); + err = initfsencoding(interp); + if (_Py_INIT_FAILED(err)) { + goto init_failed; + } + + if (initstdio() < 0) { + err = _Py_INIT_ERR("can't initialize sys standard streams"); + goto init_failed; + } + + err = add_main_module(interp); + if (_Py_INIT_FAILED(err)) { + goto init_failed; + } + + if (!Py_NoSiteFlag) { + err = initsite(); + if (_Py_INIT_FAILED(err)) { + goto init_failed; + } + } } if (!PyErr_Occurred()) return tstate; +init_failed: + _Py_FatalInitError(err); + handle_error: /* Oops, it didn't work. Undo it all. */ @@ -1417,32 +1515,35 @@ Py_GetPythonHome(void) return home; } -/* Create __main__ module */ +/* Add the __main__ module */ -static void -initmain(PyInterpreterState *interp) +static _PyInitError +add_main_module(PyInterpreterState *interp) { PyObject *m, *d, *loader, *ann_dict; m = PyImport_AddModule("__main__"); if (m == NULL) - Py_FatalError("can't create __main__ module"); + return _Py_INIT_ERR("can't create __main__ module"); + d = PyModule_GetDict(m); ann_dict = PyDict_New(); if ((ann_dict == NULL) || (PyDict_SetItemString(d, "__annotations__", ann_dict) < 0)) { - Py_FatalError("Failed to initialize __main__.__annotations__"); + return _Py_INIT_ERR("Failed to initialize __main__.__annotations__"); } Py_DECREF(ann_dict); + if (PyDict_GetItemString(d, "__builtins__") == NULL) { PyObject *bimod = PyImport_ImportModule("builtins"); if (bimod == NULL) { - Py_FatalError("Failed to retrieve builtins module"); + return _Py_INIT_ERR("Failed to retrieve builtins module"); } if (PyDict_SetItemString(d, "__builtins__", bimod) < 0) { - Py_FatalError("Failed to initialize __main__.__builtins__"); + return _Py_INIT_ERR("Failed to initialize __main__.__builtins__"); } Py_DECREF(bimod); } + /* Main is a little special - imp.is_builtin("__main__") will return * False, but BuiltinImporter is still the most appropriate initial * setting for its __loader__ attribute. A more suitable value will @@ -1454,41 +1555,40 @@ initmain(PyInterpreterState *interp) PyObject *loader = PyObject_GetAttrString(interp->importlib, "BuiltinImporter"); if (loader == NULL) { - Py_FatalError("Failed to retrieve BuiltinImporter"); + return _Py_INIT_ERR("Failed to retrieve BuiltinImporter"); } if (PyDict_SetItemString(d, "__loader__", loader) < 0) { - Py_FatalError("Failed to initialize __main__.__loader__"); + return _Py_INIT_ERR("Failed to initialize __main__.__loader__"); } Py_DECREF(loader); } + return _Py_INIT_OK(); } -static int +static _PyInitError initfsencoding(PyInterpreterState *interp) { PyObject *codec; #ifdef MS_WINDOWS - if (Py_LegacyWindowsFSEncodingFlag) - { + if (Py_LegacyWindowsFSEncodingFlag) { Py_FileSystemDefaultEncoding = "mbcs"; Py_FileSystemDefaultEncodeErrors = "replace"; } - else - { + else { Py_FileSystemDefaultEncoding = "utf-8"; Py_FileSystemDefaultEncodeErrors = "surrogatepass"; } #else - if (Py_FileSystemDefaultEncoding == NULL) - { + if (Py_FileSystemDefaultEncoding == NULL) { Py_FileSystemDefaultEncoding = get_locale_encoding(); - if (Py_FileSystemDefaultEncoding == NULL) - Py_FatalError("Py_Initialize: Unable to get the locale encoding"); + if (Py_FileSystemDefaultEncoding == NULL) { + return _Py_INIT_ERR("Unable to get the locale encoding"); + } Py_HasFileSystemDefaultEncoding = 0; interp->fscodec_initialized = 1; - return 0; + return _Py_INIT_OK(); } #endif @@ -1498,29 +1598,25 @@ initfsencoding(PyInterpreterState *interp) /* Such error can only occurs in critical situations: no more * memory, import a module of the standard library failed, * etc. */ - return -1; + return _Py_INIT_ERR("unable to load the file system codec"); } Py_DECREF(codec); interp->fscodec_initialized = 1; - return 0; + return _Py_INIT_OK(); } /* Import the site module (not into __main__ though) */ -static void +static _PyInitError initsite(void) { PyObject *m; m = PyImport_ImportModule("site"); if (m == NULL) { - fprintf(stderr, "Failed to import the site module\n"); - PyErr_Print(); - Py_Finalize(); - exit(1); - } - else { - Py_DECREF(m); + return _Py_INIT_USER_ERR("Failed to import the site module"); } + Py_DECREF(m); + return _Py_INIT_OK(); } /* Check if a file descriptor is valid or not. @@ -1926,8 +2022,8 @@ fatal_output_debug(const char *msg) } #endif -void -Py_FatalError(const char *msg) +static void +fatal_error(const char *prefix, const char *msg, int status) { const int fd = fileno(stderr); static int reentrant = 0; @@ -1939,7 +2035,18 @@ Py_FatalError(const char *msg) } reentrant = 1; - fprintf(stderr, "Fatal Python error: %s\n", msg); + fprintf(stderr, "Fatal Python error: "); + if (prefix) { + fputs(prefix, stderr); + fputs(": ", stderr); + } + if (msg) { + fputs(msg, stderr); + } + else { + fprintf(stderr, "<message not set>"); + } + fputs("\n", stderr); fflush(stderr); /* it helps in Windows debug build */ /* Print the exception (if an exception is set) with its traceback, @@ -1965,10 +2072,30 @@ Py_FatalError(const char *msg) #endif /* MS_WINDOWS */ exit: + if (status < 0) { #if defined(MS_WINDOWS) && defined(_DEBUG) - DebugBreak(); + DebugBreak(); #endif - abort(); + abort(); + } + else { + exit(status); + } +} + +void +Py_FatalError(const char *msg) +{ + fatal_error(NULL, msg, -1); +} + +void +_Py_FatalInitError(_PyInitError err) +{ + /* On "user" error: exit with status 1. + For all other errors, call abort(). */ + int status = err.user_err ? 1 : -1; + fatal_error(err.prefix, err.msg, status); } /* Clean up and exit */ @@ -2045,7 +2172,7 @@ Py_Exit(int sts) exit(sts); } -static void +static _PyInitError initsigs(void) { #ifdef SIGPIPE @@ -2059,8 +2186,9 @@ initsigs(void) #endif PyOS_InitInterrupts(); /* May imply initsignal() */ if (PyErr_Occurred()) { - Py_FatalError("Py_Initialize: can't import signal"); + return _Py_INIT_ERR("can't import signal"); } + return _Py_INIT_OK(); } diff --git a/Python/pystate.c b/Python/pystate.c index 55ff649..30f214e 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -35,7 +35,7 @@ to avoid the expense of doing their own locking). extern "C" { #endif -void +_PyInitError _PyRuntimeState_Init(_PyRuntimeState *runtime) { memset(runtime, 0, sizeof(*runtime)); @@ -46,17 +46,19 @@ _PyRuntimeState_Init(_PyRuntimeState *runtime) _PyEval_Initialize(&runtime->ceval); runtime->gilstate.check_enabled = 1; + /* A TSS key must be initialized with Py_tss_NEEDS_INIT in accordance with the specification. */ - { - Py_tss_t initial = Py_tss_NEEDS_INIT; - runtime->gilstate.autoTSSkey = initial; - } + Py_tss_t initial = Py_tss_NEEDS_INIT; + runtime->gilstate.autoTSSkey = initial; runtime->interpreters.mutex = PyThread_allocate_lock(); - if (runtime->interpreters.mutex == NULL) - Py_FatalError("Can't initialize threads for interpreter"); + if (runtime->interpreters.mutex == NULL) { + return _Py_INIT_ERR("Can't initialize threads for interpreter"); + } + runtime->interpreters.next_id = -1; + return _Py_INIT_OK(); } void diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 6dc8e08..d786739 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -1582,13 +1582,23 @@ PySys_ResetWarnOptions(void) PyList_SetSlice(warnoptions, 0, PyList_GET_SIZE(warnoptions), NULL); } -void -PySys_AddWarnOptionUnicode(PyObject *unicode) +int +_PySys_AddWarnOptionWithError(PyObject *option) { PyObject *warnoptions = get_warnoptions(); - if (warnoptions == NULL) - return; - PyList_Append(warnoptions, unicode); + if (warnoptions == NULL) { + return -1; + } + if (PyList_Append(warnoptions, option)) { + return -1; + } + return 0; +} + +void +PySys_AddWarnOptionUnicode(PyObject *option) +{ + (void)_PySys_AddWarnOptionWithError(option); } void @@ -1627,18 +1637,17 @@ get_xoptions(void) return xoptions; } -void -PySys_AddXOption(const wchar_t *s) +int +_PySys_AddXOptionWithError(const wchar_t *s) { - PyObject *opts; PyObject *name = NULL, *value = NULL; - const wchar_t *name_end; - opts = get_xoptions(); - if (opts == NULL) + PyObject *opts = get_xoptions(); + if (opts == NULL) { goto error; + } - name_end = wcschr(s, L'='); + const wchar_t *name_end = wcschr(s, L'='); if (!name_end) { name = PyUnicode_FromWideChar(s, -1); value = Py_True; @@ -1648,19 +1657,30 @@ PySys_AddXOption(const wchar_t *s) name = PyUnicode_FromWideChar(s, name_end - s); value = PyUnicode_FromWideChar(name_end + 1, -1); } - if (name == NULL || value == NULL) + if (name == NULL || value == NULL) { + goto error; + } + if (PyDict_SetItem(opts, name, value) < 0) { goto error; - PyDict_SetItem(opts, name, value); + } Py_DECREF(name); Py_DECREF(value); - return; + return 0; error: Py_XDECREF(name); Py_XDECREF(value); - /* No return value, therefore clear error state if possible */ - if (_PyThreadState_UncheckedGet()) { - PyErr_Clear(); + return -1; +} + +void +PySys_AddXOption(const wchar_t *s) +{ + if (_PySys_AddXOptionWithError(s) < 0) { + /* No return value, therefore clear error state if possible */ + if (_PyThreadState_UncheckedGet()) { + PyErr_Clear(); + } } } @@ -1999,50 +2019,52 @@ static struct PyModuleDef sysmodule = { #define SET_SYS_FROM_STRING_BORROW(key, value) \ do { \ PyObject *v = (value); \ - if (v == NULL) \ - return NULL; \ + if (v == NULL) { \ + goto err_occurred; \ + } \ res = PyDict_SetItemString(sysdict, key, v); \ if (res < 0) { \ - return NULL; \ + goto err_occurred; \ } \ } while (0) #define SET_SYS_FROM_STRING(key, value) \ do { \ PyObject *v = (value); \ - if (v == NULL) \ - return NULL; \ + if (v == NULL) { \ + goto err_occurred; \ + } \ res = PyDict_SetItemString(sysdict, key, v); \ Py_DECREF(v); \ if (res < 0) { \ - return NULL; \ + goto err_occurred; \ } \ } while (0) -PyObject * -_PySys_BeginInit(void) + +_PyInitError +_PySys_BeginInit(PyObject **sysmod) { PyObject *m, *sysdict, *version_info; int res; m = _PyModule_CreateInitialized(&sysmodule, PYTHON_API_VERSION); - if (m == NULL) - return NULL; + if (m == NULL) { + return _Py_INIT_ERR("failed to create a module object"); + } sysdict = PyModule_GetDict(m); /* Check that stdin is not a directory - Using shell redirection, you can redirect stdin to a directory, - crashing the Python interpreter. Catch this common mistake here - and output a useful error message. Note that under MS Windows, - the shell already prevents that. */ -#if !defined(MS_WINDOWS) + Using shell redirection, you can redirect stdin to a directory, + crashing the Python interpreter. Catch this common mistake here + and output a useful error message. Note that under MS Windows, + the shell already prevents that. */ +#ifndef MS_WINDOWS { struct _Py_stat_struct sb; if (_Py_fstat_noraise(fileno(stdin), &sb) == 0 && S_ISDIR(sb.st_mode)) { - /* There's nothing more we can do. */ - /* Py_FatalError() will core dump, so just exit. */ - PySys_WriteStderr("Python error: <stdin> is a directory, cannot continue\n"); - exit(EXIT_FAILURE); + return _Py_INIT_USER_ERR("<stdin> is a directory, " + "cannot continue"); } } #endif @@ -2078,8 +2100,9 @@ _PySys_BeginInit(void) PyLong_GetInfo()); /* initialize hash_info */ if (Hash_InfoType.tp_name == NULL) { - if (PyStructSequence_InitType2(&Hash_InfoType, &hash_info_desc) < 0) - return NULL; + if (PyStructSequence_InitType2(&Hash_InfoType, &hash_info_desc) < 0) { + goto type_init_failed; + } } SET_SYS_FROM_STRING("hash_info", get_hash_info()); @@ -2109,8 +2132,9 @@ _PySys_BeginInit(void) /* version_info */ if (VersionInfoType.tp_name == NULL) { if (PyStructSequence_InitType2(&VersionInfoType, - &version_info_desc) < 0) - return NULL; + &version_info_desc) < 0) { + goto type_init_failed; + } } version_info = make_version_info(); SET_SYS_FROM_STRING("version_info", version_info); @@ -2126,8 +2150,9 @@ _PySys_BeginInit(void) /* flags */ if (FlagsType.tp_name == 0) { - if (PyStructSequence_InitType2(&FlagsType, &flags_desc) < 0) - return NULL; + if (PyStructSequence_InitType2(&FlagsType, &flags_desc) < 0) { + goto type_init_failed; + } } /* Set flags to their default values */ SET_SYS_FROM_STRING("flags", make_flags()); @@ -2136,14 +2161,17 @@ _PySys_BeginInit(void) /* getwindowsversion */ if (WindowsVersionType.tp_name == 0) if (PyStructSequence_InitType2(&WindowsVersionType, - &windows_version_desc) < 0) - return NULL; + &windows_version_desc) < 0) { + goto type_init_failed; + } /* prevent user from creating new instances */ WindowsVersionType.tp_init = NULL; WindowsVersionType.tp_new = NULL; + assert(!PyErr_Occurred()); res = PyDict_DelItemString(WindowsVersionType.tp_dict, "__new__"); - if (res < 0 && PyErr_ExceptionMatches(PyExc_KeyError)) + if (res < 0 && PyErr_ExceptionMatches(PyExc_KeyError)) { PyErr_Clear(); + } #endif /* float repr style: 0.03 (short) vs 0.029999999999999999 (legacy) */ @@ -2161,13 +2189,22 @@ _PySys_BeginInit(void) if (AsyncGenHooksType.tp_name == NULL) { if (PyStructSequence_InitType2( &AsyncGenHooksType, &asyncgen_hooks_desc) < 0) { - return NULL; + goto type_init_failed; } } - if (PyErr_Occurred()) - return NULL; - return m; + if (PyErr_Occurred()) { + goto err_occurred; + } + + *sysmod = m; + return _Py_INIT_OK(); + +type_init_failed: + return _Py_INIT_ERR("failed to initialize a type"); + +err_occurred: + return _Py_INIT_ERR("can't initialize sys module"); } #undef SET_SYS_FROM_STRING |