Skip to content

Commit f7e5b56

Browse files
authored
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)
1 parent 43605e6 commit f7e5b56

File tree

18 files changed

+1332
-643
lines changed

18 files changed

+1332
-643
lines changed

Include/import.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ extern "C" {
88
#endif
99

1010
#ifndef Py_LIMITED_API
11-
PyAPI_FUNC(void) _PyImportZip_Init(void);
11+
PyAPI_FUNC(_PyInitError) _PyImportZip_Init(void);
1212

1313
PyMODINIT_FUNC PyInit_imp(void);
1414
#endif /* !Py_LIMITED_API */

Include/internal/pystate.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,16 @@ typedef struct pyruntimestate {
7474
// XXX Consolidate globals found via the check-c-globals script.
7575
} _PyRuntimeState;
7676

77+
#define _PyRuntimeState_INIT {.initialized = 0, .core_initialized = 0}
78+
7779
PyAPI_DATA(_PyRuntimeState) _PyRuntime;
78-
PyAPI_FUNC(void) _PyRuntimeState_Init(_PyRuntimeState *);
80+
PyAPI_FUNC(_PyInitError) _PyRuntimeState_Init(_PyRuntimeState *);
7981
PyAPI_FUNC(void) _PyRuntimeState_Fini(_PyRuntimeState *);
8082

83+
/* Initialize _PyRuntimeState.
84+
Return NULL on success, or return an error message on failure. */
85+
PyAPI_FUNC(_PyInitError) _PyRuntime_Initialize(void);
86+
8187
#define _Py_CURRENTLY_FINALIZING(tstate) \
8288
(_PyRuntime.finalizing == tstate)
8389

Include/pylifecycle.h

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,44 @@ PyAPI_FUNC(wchar_t *) Py_GetPythonHome(void);
2020
PyAPI_FUNC(int) Py_SetStandardStreamEncoding(const char *encoding,
2121
const char *errors);
2222

23+
typedef struct {
24+
const char *prefix;
25+
const char *msg;
26+
int user_err;
27+
} _PyInitError;
28+
29+
/* Almost all errors causing Python initialization to fail */
30+
#ifdef _MSC_VER
31+
/* Visual Studio 2015 doesn't implement C99 __func__ in C */
32+
# define _Py_INIT_GET_FUNC() __FUNCTION__
33+
#else
34+
# define _Py_INIT_GET_FUNC() __func__
35+
#endif
36+
37+
#define _Py_INIT_OK() \
38+
(_PyInitError){.prefix = NULL, .msg = NULL, .user_err = 0}
39+
#define _Py_INIT_ERR(MSG) \
40+
(_PyInitError){.prefix = _Py_INIT_GET_FUNC(), .msg = (MSG), .user_err = 0}
41+
/* Error that can be fixed by the user like invalid input parameter.
42+
Don't abort() the process on such error. */
43+
#define _Py_INIT_USER_ERR(MSG) \
44+
(_PyInitError){.prefix = _Py_INIT_GET_FUNC(), .msg = (MSG), .user_err = 1}
45+
#define _Py_INIT_FAILED(err) \
46+
(err.msg != NULL)
47+
2348
/* PEP 432 Multi-phase initialization API (Private while provisional!) */
24-
PyAPI_FUNC(void) _Py_InitializeCore(const _PyCoreConfig *);
49+
PyAPI_FUNC(_PyInitError) _Py_InitializeCore(const _PyCoreConfig *);
2550
PyAPI_FUNC(int) _Py_IsCoreInitialized(void);
26-
PyAPI_FUNC(int) _Py_ReadMainInterpreterConfig(_PyMainInterpreterConfig *);
27-
PyAPI_FUNC(int) _Py_InitializeMainInterpreter(const _PyMainInterpreterConfig *);
51+
PyAPI_FUNC(_PyInitError) _Py_ReadMainInterpreterConfig(_PyMainInterpreterConfig *);
52+
PyAPI_FUNC(_PyInitError) _Py_InitializeMainInterpreter(const _PyMainInterpreterConfig *);
2853
#endif
2954

3055
/* Initialization and finalization */
3156
PyAPI_FUNC(void) Py_Initialize(void);
3257
PyAPI_FUNC(void) Py_InitializeEx(int);
3358
#ifndef Py_LIMITED_API
34-
PyAPI_FUNC(void) _Py_InitializeEx_Private(int, int);
59+
PyAPI_FUNC(_PyInitError) _Py_InitializeEx_Private(int, int);
60+
PyAPI_FUNC(void) _Py_FatalInitError(_PyInitError err) _Py_NO_RETURN;
3561
#endif
3662
PyAPI_FUNC(void) Py_Finalize(void);
3763
PyAPI_FUNC(int) Py_FinalizeEx(void);
@@ -50,7 +76,7 @@ PyAPI_FUNC(void) _Py_PyAtExit(void (*func)(void));
5076
#endif
5177
PyAPI_FUNC(int) Py_AtExit(void (*func)(void));
5278

53-
PyAPI_FUNC(void) Py_Exit(int);
79+
PyAPI_FUNC(void) Py_Exit(int) _Py_NO_RETURN;
5480

5581
/* Restore signals that the interpreter has called SIG_IGN on to SIG_DFL. */
5682
#ifndef Py_LIMITED_API
@@ -86,15 +112,15 @@ PyAPI_FUNC(const char *) _Py_gitversion(void);
86112
/* Internal -- various one-time initializations */
87113
#ifndef Py_LIMITED_API
88114
PyAPI_FUNC(PyObject *) _PyBuiltin_Init(void);
89-
PyAPI_FUNC(PyObject *) _PySys_BeginInit(void);
115+
PyAPI_FUNC(_PyInitError) _PySys_BeginInit(PyObject **sysmod);
90116
PyAPI_FUNC(int) _PySys_EndInit(PyObject *sysdict);
91-
PyAPI_FUNC(void) _PyImport_Init(void);
117+
PyAPI_FUNC(_PyInitError) _PyImport_Init(void);
92118
PyAPI_FUNC(void) _PyExc_Init(PyObject * bltinmod);
93-
PyAPI_FUNC(void) _PyImportHooks_Init(void);
119+
PyAPI_FUNC(_PyInitError) _PyImportHooks_Init(void);
94120
PyAPI_FUNC(int) _PyFrame_Init(void);
95121
PyAPI_FUNC(int) _PyFloat_Init(void);
96122
PyAPI_FUNC(int) PyByteArray_Init(void);
97-
PyAPI_FUNC(void) _Py_HashRandomization_Init(_PyCoreConfig *core_config);
123+
PyAPI_FUNC(_PyInitError) _Py_HashRandomization_Init(_PyCoreConfig *core_config);
98124
#endif
99125

100126
/* Various internal finalizers */

Include/pymem.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,10 @@ PyAPI_FUNC(void) PyMem_SetAllocator(PyMemAllocatorDomain domain,
223223
PyAPI_FUNC(void) PyMem_SetupDebugHooks(void);
224224
#endif
225225

226+
#ifdef Py_BUILD_CORE
227+
PyAPI_FUNC(void) _PyMem_GetDefaultRawAllocator(PyMemAllocatorEx *alloc);
228+
#endif
229+
226230
#ifdef __cplusplus
227231
}
228232
#endif

Include/sysmodule.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ PyAPI_FUNC(PyObject *) PySys_GetXOptions(void);
3737
PyAPI_FUNC(size_t) _PySys_GetSizeOf(PyObject *);
3838
#endif
3939

40+
#ifdef Py_BUILD_CORE
41+
PyAPI_FUNC(int) _PySys_AddXOptionWithError(const wchar_t *s);
42+
PyAPI_FUNC(int) _PySys_AddWarnOptionWithError(PyObject *option);
43+
#endif
44+
4045
#ifdef __cplusplus
4146
}
4247
#endif

Modules/faulthandler.c

Lines changed: 47 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1281,47 +1281,54 @@ PyInit_faulthandler(void)
12811281
return m;
12821282
}
12831283

1284-
/* Call faulthandler.enable() if the PYTHONFAULTHANDLER environment variable
1285-
is defined, or if sys._xoptions has a 'faulthandler' key. */
1286-
12871284
static int
1288-
faulthandler_env_options(void)
1285+
faulthandler_init_enable(void)
12891286
{
1290-
PyObject *xoptions, *key, *module, *res;
1291-
char *p;
1287+
PyObject *module = PyImport_ImportModule("faulthandler");
1288+
if (module == NULL) {
1289+
return -1;
1290+
}
12921291

1293-
if (!((p = Py_GETENV("PYTHONFAULTHANDLER")) && *p != '\0')) {
1294-
/* PYTHONFAULTHANDLER environment variable is missing
1295-
or an empty string */
1296-
int has_key;
1292+
PyObject *res = _PyObject_CallMethodId(module, &PyId_enable, NULL);
1293+
Py_DECREF(module);
1294+
if (res == NULL) {
1295+
return -1;
1296+
}
1297+
Py_DECREF(res);
12971298

1298-
xoptions = PySys_GetXOptions();
1299-
if (xoptions == NULL)
1300-
return -1;
1299+
return 0;
1300+
}
13011301

1302-
key = PyUnicode_FromString("faulthandler");
1303-
if (key == NULL)
1304-
return -1;
1302+
/* Call faulthandler.enable() if the PYTHONFAULTHANDLER environment variable
1303+
is defined, or if sys._xoptions has a 'faulthandler' key. */
13051304

1306-
has_key = PyDict_Contains(xoptions, key);
1307-
Py_DECREF(key);
1308-
if (has_key <= 0)
1309-
return has_key;
1305+
static int
1306+
faulthandler_init_parse(void)
1307+
{
1308+
char *p = Py_GETENV("PYTHONFAULTHANDLER");
1309+
if (p && *p != '\0') {
1310+
return 1;
13101311
}
13111312

1312-
module = PyImport_ImportModule("faulthandler");
1313-
if (module == NULL) {
1313+
/* PYTHONFAULTHANDLER environment variable is missing
1314+
or an empty string */
1315+
PyObject *xoptions = PySys_GetXOptions();
1316+
if (xoptions == NULL) {
13141317
return -1;
13151318
}
1316-
res = _PyObject_CallMethodId(module, &PyId_enable, NULL);
1317-
Py_DECREF(module);
1318-
if (res == NULL)
1319+
1320+
PyObject *key = PyUnicode_FromString("faulthandler");
1321+
if (key == NULL) {
13191322
return -1;
1320-
Py_DECREF(res);
1321-
return 0;
1323+
}
1324+
1325+
int has_key = PyDict_Contains(xoptions, key);
1326+
Py_DECREF(key);
1327+
return has_key;
13221328
}
13231329

1324-
int _PyFaulthandler_Init(void)
1330+
_PyInitError
1331+
_PyFaulthandler_Init(void)
13251332
{
13261333
#ifdef HAVE_SIGALTSTACK
13271334
int err;
@@ -1345,14 +1352,22 @@ int _PyFaulthandler_Init(void)
13451352
thread.cancel_event = PyThread_allocate_lock();
13461353
thread.running = PyThread_allocate_lock();
13471354
if (!thread.cancel_event || !thread.running) {
1348-
PyErr_SetString(PyExc_RuntimeError,
1349-
"could not allocate locks for faulthandler");
1350-
return -1;
1355+
return _Py_INIT_ERR("failed to allocate locks for faulthandler");
13511356
}
13521357
PyThread_acquire_lock(thread.cancel_event, 1);
13531358
#endif
13541359

1355-
return faulthandler_env_options();
1360+
int enable = faulthandler_init_parse();
1361+
if (enable < 0) {
1362+
return _Py_INIT_ERR("failed to parse faulthandler env var and cmdline");
1363+
}
1364+
1365+
if (enable) {
1366+
if (faulthandler_init_enable() < 0) {
1367+
return _Py_INIT_ERR("failed to enable faulthandler");
1368+
}
1369+
}
1370+
return _Py_INIT_OK();
13561371
}
13571372

13581373
void _PyFaulthandler_Fini(void)

0 commit comments

Comments
 (0)