Skip to content

Commit db71975

Browse files
authored
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.
1 parent c4e671e commit db71975

File tree

8 files changed

+57
-45
lines changed

8 files changed

+57
-45
lines changed

Include/cpython/coreconfig.h

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,18 @@ extern "C" {
88
/* --- _PyInitError ----------------------------------------------- */
99

1010
typedef struct {
11-
const char *prefix;
12-
const char *msg;
13-
int user_err;
11+
enum {
12+
_Py_INIT_ERR_TYPE_OK=0,
13+
_Py_INIT_ERR_TYPE_ERROR=1,
14+
_Py_INIT_ERR_TYPE_EXIT=2
15+
} _type;
16+
const char *_func;
17+
const char *err_msg;
18+
#ifdef MS_WINDOWS
19+
unsigned int exitcode;
20+
#else
1421
int exitcode;
22+
#endif
1523
} _PyInitError;
1624

1725
/* Almost all errors causing Python initialization to fail */
@@ -23,20 +31,25 @@ typedef struct {
2331
#endif
2432

2533
#define _Py_INIT_OK() \
26-
(_PyInitError){.prefix = NULL, .msg = NULL, .user_err = 0, .exitcode = -1}
27-
#define _Py_INIT_ERR(MSG) \
28-
(_PyInitError){.prefix = _Py_INIT_GET_FUNC(), .msg = (MSG), .user_err = 0, .exitcode = -1}
29-
/* Error that can be fixed by the user like invalid input parameter.
30-
Don't abort() the process on such error. */
31-
#define _Py_INIT_USER_ERR(MSG) \
32-
(_PyInitError){.prefix = _Py_INIT_GET_FUNC(), .msg = (MSG), .user_err = 1, .exitcode = -1}
33-
#define _Py_INIT_NO_MEMORY() _Py_INIT_USER_ERR("memory allocation failed")
34+
(_PyInitError){._type = _Py_INIT_ERR_TYPE_OK,}
35+
/* other fields are set to 0 */
36+
#define _Py_INIT_ERR(ERR_MSG) \
37+
(_PyInitError){ \
38+
._type = _Py_INIT_ERR_TYPE_ERROR, \
39+
._func = _Py_INIT_GET_FUNC(), \
40+
.err_msg = (ERR_MSG)}
41+
/* other fields are set to 0 */
42+
#define _Py_INIT_NO_MEMORY() _Py_INIT_ERR("memory allocation failed")
3443
#define _Py_INIT_EXIT(EXITCODE) \
35-
(_PyInitError){.prefix = NULL, .msg = NULL, .user_err = 0, .exitcode = (EXITCODE)}
36-
#define _Py_INIT_HAS_EXITCODE(err) \
37-
(err.exitcode != -1)
44+
(_PyInitError){ \
45+
._type = _Py_INIT_ERR_TYPE_EXIT, \
46+
.exitcode = (EXITCODE)}
47+
#define _Py_INIT_IS_ERROR(err) \
48+
(err._type == _Py_INIT_ERR_TYPE_ERROR)
49+
#define _Py_INIT_IS_EXIT(err) \
50+
(err._type == _Py_INIT_ERR_TYPE_EXIT)
3851
#define _Py_INIT_FAILED(err) \
39-
(err.msg != NULL || _Py_INIT_HAS_EXITCODE(err))
52+
(err._type != _Py_INIT_ERR_TYPE_OK)
4053

4154
/* --- _PyWstrList ------------------------------------------------ */
4255

Modules/getpath.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,10 +114,10 @@ extern "C" {
114114

115115
#define DECODE_LOCALE_ERR(NAME, LEN) \
116116
((LEN) == (size_t)-2) \
117-
? _Py_INIT_USER_ERR("cannot decode " NAME) \
117+
? _Py_INIT_ERR("cannot decode " NAME) \
118118
: _Py_INIT_NO_MEMORY()
119119

120-
#define PATHLEN_ERR() _Py_INIT_USER_ERR("path configuration: path too long")
120+
#define PATHLEN_ERR() _Py_INIT_ERR("path configuration: path too long")
121121

122122
typedef struct {
123123
wchar_t *path_env; /* PATH environment variable */

Modules/main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -570,7 +570,7 @@ exit_sigint(void)
570570
static void _Py_NO_RETURN
571571
pymain_exit_error(_PyInitError err)
572572
{
573-
if (_Py_INIT_HAS_EXITCODE(err)) {
573+
if (_Py_INIT_IS_EXIT(err)) {
574574
/* If it's an error rather than a regular exit, leave Python runtime
575575
alive: _Py_ExitInitError() uses the current exception and use
576576
sys.stdout in this case. */

Python/bootstrap_hash.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -578,8 +578,8 @@ _Py_HashRandomization_Init(const _PyCoreConfig *config)
578578
pyurandom() is non-blocking mode (blocking=0): see the PEP 524. */
579579
res = pyurandom(secret, secret_size, 0, 0);
580580
if (res < 0) {
581-
return _Py_INIT_USER_ERR("failed to get random numbers "
582-
"to initialize Python");
581+
return _Py_INIT_ERR("failed to get random numbers "
582+
"to initialize Python");
583583
}
584584
}
585585
return _Py_INIT_OK();

Python/coreconfig.c

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,7 @@ Py_GetArgcArgv(int *argc, wchar_t ***argv)
475475

476476
#define DECODE_LOCALE_ERR(NAME, LEN) \
477477
(((LEN) == -2) \
478-
? _Py_INIT_USER_ERR("cannot decode " NAME) \
478+
? _Py_INIT_ERR("cannot decode " NAME) \
479479
: _Py_INIT_NO_MEMORY())
480480

481481
/* Free memory allocated in config, but don't clear all attributes */
@@ -1018,8 +1018,8 @@ config_init_hash_seed(_PyCoreConfig *config)
10181018
|| seed > 4294967295UL
10191019
|| (errno == ERANGE && seed == ULONG_MAX))
10201020
{
1021-
return _Py_INIT_USER_ERR("PYTHONHASHSEED must be \"random\" "
1022-
"or an integer in range [0; 4294967295]");
1021+
return _Py_INIT_ERR("PYTHONHASHSEED must be \"random\" "
1022+
"or an integer in range [0; 4294967295]");
10231023
}
10241024
/* Use a specific hash */
10251025
config->use_hash_seed = 1;
@@ -1129,8 +1129,7 @@ config_init_tracemalloc(_PyCoreConfig *config)
11291129
valid = 0;
11301130
}
11311131
if (!valid) {
1132-
return _Py_INIT_USER_ERR("PYTHONTRACEMALLOC: invalid number "
1133-
"of frames");
1132+
return _Py_INIT_ERR("PYTHONTRACEMALLOC: invalid number of frames");
11341133
}
11351134
config->tracemalloc = nframe;
11361135
}
@@ -1146,8 +1145,8 @@ config_init_tracemalloc(_PyCoreConfig *config)
11461145
valid = 0;
11471146
}
11481147
if (!valid) {
1149-
return _Py_INIT_USER_ERR("-X tracemalloc=NFRAME: "
1150-
"invalid number of frames");
1148+
return _Py_INIT_ERR("-X tracemalloc=NFRAME: "
1149+
"invalid number of frames");
11511150
}
11521151
}
11531152
else {
@@ -1267,8 +1266,8 @@ config_get_locale_encoding(char **locale_encoding)
12671266
#else
12681267
const char *encoding = nl_langinfo(CODESET);
12691268
if (!encoding || encoding[0] == '\0') {
1270-
return _Py_INIT_USER_ERR("failed to get the locale encoding: "
1271-
"nl_langinfo(CODESET) failed");
1269+
return _Py_INIT_ERR("failed to get the locale encoding: "
1270+
"nl_langinfo(CODESET) failed");
12721271
}
12731272
#endif
12741273
*locale_encoding = _PyMem_RawStrdup(encoding);

Python/frozenmain.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,7 @@ Py_FrozenMain(int argc, char **argv)
1818
{
1919
_PyInitError err = _PyRuntime_Initialize();
2020
if (_Py_INIT_FAILED(err)) {
21-
fprintf(stderr, "Fatal Python error: %s\n", err.msg);
22-
fflush(stderr);
23-
exit(1);
21+
_Py_ExitInitError(err);
2422
}
2523

2624
const char *p;

Python/preconfig.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
#define DECODE_LOCALE_ERR(NAME, LEN) \
99
(((LEN) == -2) \
10-
? _Py_INIT_USER_ERR("cannot decode " NAME) \
10+
? _Py_INIT_ERR("cannot decode " NAME) \
1111
: _Py_INIT_NO_MEMORY())
1212

1313

@@ -526,7 +526,7 @@ preconfig_init_utf8_mode(_PyPreConfig *config, const _PyPreCmdline *cmdline)
526526
config->utf8_mode = 0;
527527
}
528528
else {
529-
return _Py_INIT_USER_ERR("invalid -X utf8 option value");
529+
return _Py_INIT_ERR("invalid -X utf8 option value");
530530
}
531531
}
532532
else {
@@ -544,8 +544,8 @@ preconfig_init_utf8_mode(_PyPreConfig *config, const _PyPreCmdline *cmdline)
544544
config->utf8_mode = 0;
545545
}
546546
else {
547-
return _Py_INIT_USER_ERR("invalid PYTHONUTF8 environment "
548-
"variable value");
547+
return _Py_INIT_ERR("invalid PYTHONUTF8 environment "
548+
"variable value");
549549
}
550550
return _Py_INIT_OK();
551551
}
@@ -831,7 +831,7 @@ _PyPreConfig_SetAllocator(_PyPreConfig *config)
831831
PyMem_GetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
832832

833833
if (_PyMem_SetupAllocators(config->allocator) < 0) {
834-
return _Py_INIT_USER_ERR("Unknown PYTHONMALLOC allocator");
834+
return _Py_INIT_ERR("Unknown PYTHONMALLOC allocator");
835835
}
836836

837837
/* Copy the pre-configuration with the new allocator */

Python/pylifecycle.c

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1685,7 +1685,7 @@ initsite(void)
16851685
PyObject *m;
16861686
m = PyImport_ImportModule("site");
16871687
if (m == NULL) {
1688-
return _Py_INIT_USER_ERR("Failed to import the site module");
1688+
return _Py_INIT_ERR("Failed to import the site module");
16891689
}
16901690
Py_DECREF(m);
16911691
return _Py_INIT_OK();
@@ -1872,8 +1872,7 @@ init_sys_streams(PyInterpreterState *interp)
18721872
struct _Py_stat_struct sb;
18731873
if (_Py_fstat_noraise(fileno(stdin), &sb) == 0 &&
18741874
S_ISDIR(sb.st_mode)) {
1875-
return _Py_INIT_USER_ERR("<stdin> is a directory, "
1876-
"cannot continue");
1875+
return _Py_INIT_ERR("<stdin> is a directory, cannot continue");
18771876
}
18781877
#endif
18791878

@@ -2181,14 +2180,17 @@ Py_FatalError(const char *msg)
21812180
void _Py_NO_RETURN
21822181
_Py_ExitInitError(_PyInitError err)
21832182
{
2184-
if (_Py_INIT_HAS_EXITCODE(err)) {
2183+
assert(_Py_INIT_FAILED(err));
2184+
if (_Py_INIT_IS_EXIT(err)) {
2185+
#ifdef MS_WINDOWS
2186+
ExitProcess(err.exitcode);
2187+
#else
21852188
exit(err.exitcode);
2189+
#endif
21862190
}
21872191
else {
2188-
/* On "user" error: exit with status 1.
2189-
For all other errors, call abort(). */
2190-
int status = err.user_err ? 1 : -1;
2191-
fatal_error(err.prefix, err.msg, status);
2192+
assert(_Py_INIT_IS_ERROR(err));
2193+
fatal_error(err._func, err.err_msg, 1);
21922194
}
21932195
}
21942196

0 commit comments

Comments
 (0)