bpo-36763: Rework _PyInitError API (GH-13031)

* Remove _PyInitError.user_err field and _Py_INIT_USER_ERR() macro:
  use _Py_INIT_ERR() instead. _Py_ExitInitError() now longer calls
  abort() on error: exit with exit code 1 instead.
* Add _PyInitError._type private field.
* exitcode field type is now unsigned int on Windows.
* Rename prefix field to _func.
* Rename msg field to err_msg.
This commit is contained in:
Victor Stinner 2019-05-01 05:35:33 +02:00 committed by GitHub
parent c4e671eec2
commit db71975431
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 57 additions and 45 deletions

View File

@ -8,10 +8,18 @@ extern "C" {
/* --- _PyInitError ----------------------------------------------- */ /* --- _PyInitError ----------------------------------------------- */
typedef struct { typedef struct {
const char *prefix; enum {
const char *msg; _Py_INIT_ERR_TYPE_OK=0,
int user_err; _Py_INIT_ERR_TYPE_ERROR=1,
_Py_INIT_ERR_TYPE_EXIT=2
} _type;
const char *_func;
const char *err_msg;
#ifdef MS_WINDOWS
unsigned int exitcode;
#else
int exitcode; int exitcode;
#endif
} _PyInitError; } _PyInitError;
/* Almost all errors causing Python initialization to fail */ /* Almost all errors causing Python initialization to fail */
@ -23,20 +31,25 @@ typedef struct {
#endif #endif
#define _Py_INIT_OK() \ #define _Py_INIT_OK() \
(_PyInitError){.prefix = NULL, .msg = NULL, .user_err = 0, .exitcode = -1} (_PyInitError){._type = _Py_INIT_ERR_TYPE_OK,}
#define _Py_INIT_ERR(MSG) \ /* other fields are set to 0 */
(_PyInitError){.prefix = _Py_INIT_GET_FUNC(), .msg = (MSG), .user_err = 0, .exitcode = -1} #define _Py_INIT_ERR(ERR_MSG) \
/* Error that can be fixed by the user like invalid input parameter. (_PyInitError){ \
Don't abort() the process on such error. */ ._type = _Py_INIT_ERR_TYPE_ERROR, \
#define _Py_INIT_USER_ERR(MSG) \ ._func = _Py_INIT_GET_FUNC(), \
(_PyInitError){.prefix = _Py_INIT_GET_FUNC(), .msg = (MSG), .user_err = 1, .exitcode = -1} .err_msg = (ERR_MSG)}
#define _Py_INIT_NO_MEMORY() _Py_INIT_USER_ERR("memory allocation failed") /* other fields are set to 0 */
#define _Py_INIT_NO_MEMORY() _Py_INIT_ERR("memory allocation failed")
#define _Py_INIT_EXIT(EXITCODE) \ #define _Py_INIT_EXIT(EXITCODE) \
(_PyInitError){.prefix = NULL, .msg = NULL, .user_err = 0, .exitcode = (EXITCODE)} (_PyInitError){ \
#define _Py_INIT_HAS_EXITCODE(err) \ ._type = _Py_INIT_ERR_TYPE_EXIT, \
(err.exitcode != -1) .exitcode = (EXITCODE)}
#define _Py_INIT_IS_ERROR(err) \
(err._type == _Py_INIT_ERR_TYPE_ERROR)
#define _Py_INIT_IS_EXIT(err) \
(err._type == _Py_INIT_ERR_TYPE_EXIT)
#define _Py_INIT_FAILED(err) \ #define _Py_INIT_FAILED(err) \
(err.msg != NULL || _Py_INIT_HAS_EXITCODE(err)) (err._type != _Py_INIT_ERR_TYPE_OK)
/* --- _PyWstrList ------------------------------------------------ */ /* --- _PyWstrList ------------------------------------------------ */

View File

@ -114,10 +114,10 @@ extern "C" {
#define DECODE_LOCALE_ERR(NAME, LEN) \ #define DECODE_LOCALE_ERR(NAME, LEN) \
((LEN) == (size_t)-2) \ ((LEN) == (size_t)-2) \
? _Py_INIT_USER_ERR("cannot decode " NAME) \ ? _Py_INIT_ERR("cannot decode " NAME) \
: _Py_INIT_NO_MEMORY() : _Py_INIT_NO_MEMORY()
#define PATHLEN_ERR() _Py_INIT_USER_ERR("path configuration: path too long") #define PATHLEN_ERR() _Py_INIT_ERR("path configuration: path too long")
typedef struct { typedef struct {
wchar_t *path_env; /* PATH environment variable */ wchar_t *path_env; /* PATH environment variable */

View File

@ -570,7 +570,7 @@ exit_sigint(void)
static void _Py_NO_RETURN static void _Py_NO_RETURN
pymain_exit_error(_PyInitError err) pymain_exit_error(_PyInitError err)
{ {
if (_Py_INIT_HAS_EXITCODE(err)) { if (_Py_INIT_IS_EXIT(err)) {
/* If it's an error rather than a regular exit, leave Python runtime /* If it's an error rather than a regular exit, leave Python runtime
alive: _Py_ExitInitError() uses the current exception and use alive: _Py_ExitInitError() uses the current exception and use
sys.stdout in this case. */ sys.stdout in this case. */

View File

@ -578,8 +578,8 @@ _Py_HashRandomization_Init(const _PyCoreConfig *config)
pyurandom() is non-blocking mode (blocking=0): see the PEP 524. */ pyurandom() is non-blocking mode (blocking=0): see the PEP 524. */
res = pyurandom(secret, secret_size, 0, 0); res = pyurandom(secret, secret_size, 0, 0);
if (res < 0) { if (res < 0) {
return _Py_INIT_USER_ERR("failed to get random numbers " return _Py_INIT_ERR("failed to get random numbers "
"to initialize Python"); "to initialize Python");
} }
} }
return _Py_INIT_OK(); return _Py_INIT_OK();

View File

@ -475,7 +475,7 @@ Py_GetArgcArgv(int *argc, wchar_t ***argv)
#define DECODE_LOCALE_ERR(NAME, LEN) \ #define DECODE_LOCALE_ERR(NAME, LEN) \
(((LEN) == -2) \ (((LEN) == -2) \
? _Py_INIT_USER_ERR("cannot decode " NAME) \ ? _Py_INIT_ERR("cannot decode " NAME) \
: _Py_INIT_NO_MEMORY()) : _Py_INIT_NO_MEMORY())
/* Free memory allocated in config, but don't clear all attributes */ /* Free memory allocated in config, but don't clear all attributes */
@ -1018,8 +1018,8 @@ config_init_hash_seed(_PyCoreConfig *config)
|| seed > 4294967295UL || seed > 4294967295UL
|| (errno == ERANGE && seed == ULONG_MAX)) || (errno == ERANGE && seed == ULONG_MAX))
{ {
return _Py_INIT_USER_ERR("PYTHONHASHSEED must be \"random\" " return _Py_INIT_ERR("PYTHONHASHSEED must be \"random\" "
"or an integer in range [0; 4294967295]"); "or an integer in range [0; 4294967295]");
} }
/* Use a specific hash */ /* Use a specific hash */
config->use_hash_seed = 1; config->use_hash_seed = 1;
@ -1129,8 +1129,7 @@ config_init_tracemalloc(_PyCoreConfig *config)
valid = 0; valid = 0;
} }
if (!valid) { if (!valid) {
return _Py_INIT_USER_ERR("PYTHONTRACEMALLOC: invalid number " return _Py_INIT_ERR("PYTHONTRACEMALLOC: invalid number of frames");
"of frames");
} }
config->tracemalloc = nframe; config->tracemalloc = nframe;
} }
@ -1146,8 +1145,8 @@ config_init_tracemalloc(_PyCoreConfig *config)
valid = 0; valid = 0;
} }
if (!valid) { if (!valid) {
return _Py_INIT_USER_ERR("-X tracemalloc=NFRAME: " return _Py_INIT_ERR("-X tracemalloc=NFRAME: "
"invalid number of frames"); "invalid number of frames");
} }
} }
else { else {
@ -1267,8 +1266,8 @@ config_get_locale_encoding(char **locale_encoding)
#else #else
const char *encoding = nl_langinfo(CODESET); const char *encoding = nl_langinfo(CODESET);
if (!encoding || encoding[0] == '\0') { if (!encoding || encoding[0] == '\0') {
return _Py_INIT_USER_ERR("failed to get the locale encoding: " return _Py_INIT_ERR("failed to get the locale encoding: "
"nl_langinfo(CODESET) failed"); "nl_langinfo(CODESET) failed");
} }
#endif #endif
*locale_encoding = _PyMem_RawStrdup(encoding); *locale_encoding = _PyMem_RawStrdup(encoding);

View File

@ -18,9 +18,7 @@ Py_FrozenMain(int argc, char **argv)
{ {
_PyInitError err = _PyRuntime_Initialize(); _PyInitError err = _PyRuntime_Initialize();
if (_Py_INIT_FAILED(err)) { if (_Py_INIT_FAILED(err)) {
fprintf(stderr, "Fatal Python error: %s\n", err.msg); _Py_ExitInitError(err);
fflush(stderr);
exit(1);
} }
const char *p; const char *p;

View File

@ -7,7 +7,7 @@
#define DECODE_LOCALE_ERR(NAME, LEN) \ #define DECODE_LOCALE_ERR(NAME, LEN) \
(((LEN) == -2) \ (((LEN) == -2) \
? _Py_INIT_USER_ERR("cannot decode " NAME) \ ? _Py_INIT_ERR("cannot decode " NAME) \
: _Py_INIT_NO_MEMORY()) : _Py_INIT_NO_MEMORY())
@ -526,7 +526,7 @@ preconfig_init_utf8_mode(_PyPreConfig *config, const _PyPreCmdline *cmdline)
config->utf8_mode = 0; config->utf8_mode = 0;
} }
else { else {
return _Py_INIT_USER_ERR("invalid -X utf8 option value"); return _Py_INIT_ERR("invalid -X utf8 option value");
} }
} }
else { else {
@ -544,8 +544,8 @@ preconfig_init_utf8_mode(_PyPreConfig *config, const _PyPreCmdline *cmdline)
config->utf8_mode = 0; config->utf8_mode = 0;
} }
else { else {
return _Py_INIT_USER_ERR("invalid PYTHONUTF8 environment " return _Py_INIT_ERR("invalid PYTHONUTF8 environment "
"variable value"); "variable value");
} }
return _Py_INIT_OK(); return _Py_INIT_OK();
} }
@ -831,7 +831,7 @@ _PyPreConfig_SetAllocator(_PyPreConfig *config)
PyMem_GetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); PyMem_GetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
if (_PyMem_SetupAllocators(config->allocator) < 0) { if (_PyMem_SetupAllocators(config->allocator) < 0) {
return _Py_INIT_USER_ERR("Unknown PYTHONMALLOC allocator"); return _Py_INIT_ERR("Unknown PYTHONMALLOC allocator");
} }
/* Copy the pre-configuration with the new allocator */ /* Copy the pre-configuration with the new allocator */

View File

@ -1685,7 +1685,7 @@ initsite(void)
PyObject *m; PyObject *m;
m = PyImport_ImportModule("site"); m = PyImport_ImportModule("site");
if (m == NULL) { if (m == NULL) {
return _Py_INIT_USER_ERR("Failed to import the site module"); return _Py_INIT_ERR("Failed to import the site module");
} }
Py_DECREF(m); Py_DECREF(m);
return _Py_INIT_OK(); return _Py_INIT_OK();
@ -1872,8 +1872,7 @@ init_sys_streams(PyInterpreterState *interp)
struct _Py_stat_struct sb; struct _Py_stat_struct sb;
if (_Py_fstat_noraise(fileno(stdin), &sb) == 0 && if (_Py_fstat_noraise(fileno(stdin), &sb) == 0 &&
S_ISDIR(sb.st_mode)) { S_ISDIR(sb.st_mode)) {
return _Py_INIT_USER_ERR("<stdin> is a directory, " return _Py_INIT_ERR("<stdin> is a directory, cannot continue");
"cannot continue");
} }
#endif #endif
@ -2181,14 +2180,17 @@ Py_FatalError(const char *msg)
void _Py_NO_RETURN void _Py_NO_RETURN
_Py_ExitInitError(_PyInitError err) _Py_ExitInitError(_PyInitError err)
{ {
if (_Py_INIT_HAS_EXITCODE(err)) { assert(_Py_INIT_FAILED(err));
if (_Py_INIT_IS_EXIT(err)) {
#ifdef MS_WINDOWS
ExitProcess(err.exitcode);
#else
exit(err.exitcode); exit(err.exitcode);
#endif
} }
else { else {
/* On "user" error: exit with status 1. assert(_Py_INIT_IS_ERROR(err));
For all other errors, call abort(). */ fatal_error(err._func, err.err_msg, 1);
int status = err.user_err ? 1 : -1;
fatal_error(err.prefix, err.msg, status);
} }
} }