Skip to content

Commit 120b707

Browse files
authored
bpo-36763: PyConfig_Read() handles PySys_AddXOption() (GH-15431)
PyConfig_Read() is now responsible to handle early calls to PySys_AddXOption() and PySys_AddWarnOption(). Options added by PySys_AddXOption() are now handled the same way than PyConfig.xoptions and command line -X options. For example, PySys_AddXOption(L"faulthandler") enables faulthandler as expected.
1 parent 1beb7c3 commit 120b707

File tree

6 files changed

+115
-30
lines changed

6 files changed

+115
-30
lines changed

Include/internal/pycore_pylifecycle.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ extern PyStatus _PySys_Create(
4343
PyThreadState *tstate,
4444
PyObject **sysmod_p);
4545
extern PyStatus _PySys_SetPreliminaryStderr(PyObject *sysdict);
46+
extern PyStatus _PySys_ReadPreinitWarnOptions(PyConfig *config);
47+
extern PyStatus _PySys_ReadPreinitXOptions(PyConfig *config);
4648
extern int _PySys_InitMain(
4749
_PyRuntimeState *runtime,
4850
PyThreadState *tstate);

Lib/test/test_embed.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -902,6 +902,23 @@ def modify_path(path):
902902
api=API_PYTHON,
903903
modify_path_cb=modify_path)
904904

905+
def test_init_sys_add(self):
906+
config = {
907+
'faulthandler': 1,
908+
'xoptions': [
909+
'config_xoption',
910+
'cmdline_xoption',
911+
'sysadd_xoption',
912+
'faulthandler',
913+
],
914+
'warnoptions': [
915+
'ignore:::config_warnoption',
916+
'ignore:::cmdline_warnoption',
917+
'ignore:::sysadd_warnoption',
918+
],
919+
}
920+
self.check_all_configs("test_init_sys_add", config, api=API_PYTHON)
921+
905922
def test_init_run_main(self):
906923
code = ('import _testinternalcapi, json; '
907924
'print(json.dumps(_testinternalcapi.get_configs()))')
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Options added by ``PySys_AddXOption()`` are now handled the same way than
2+
``PyConfig.xoptions`` and command line ``-X`` options.

Programs/_testembed.c

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1375,6 +1375,54 @@ static int test_init_read_set(void)
13751375
}
13761376

13771377

1378+
static int test_init_sys_add(void)
1379+
{
1380+
PySys_AddXOption(L"sysadd_xoption");
1381+
PySys_AddXOption(L"faulthandler");
1382+
PySys_AddWarnOption(L"ignore:::sysadd_warnoption");
1383+
1384+
PyConfig config;
1385+
PyStatus status;
1386+
status = PyConfig_InitPythonConfig(&config);
1387+
if (PyStatus_Exception(status)) {
1388+
goto fail;
1389+
}
1390+
1391+
wchar_t* argv[] = {
1392+
L"python3",
1393+
L"-W",
1394+
L"ignore:::cmdline_warnoption",
1395+
L"-X",
1396+
L"cmdline_xoption",
1397+
};
1398+
config_set_argv(&config, Py_ARRAY_LENGTH(argv), argv);
1399+
config.parse_argv = 1;
1400+
1401+
status = PyWideStringList_Append(&config.xoptions,
1402+
L"config_xoption");
1403+
if (PyStatus_Exception(status)) {
1404+
goto fail;
1405+
}
1406+
1407+
status = PyWideStringList_Append(&config.warnoptions,
1408+
L"ignore:::config_warnoption");
1409+
if (PyStatus_Exception(status)) {
1410+
goto fail;
1411+
}
1412+
1413+
config_set_program_name(&config);
1414+
init_from_config_clear(&config);
1415+
1416+
dump_config();
1417+
Py_Finalize();
1418+
return 0;
1419+
1420+
fail:
1421+
PyConfig_Clear(&config);
1422+
Py_ExitStatusException(status);
1423+
}
1424+
1425+
13781426
static void configure_init_main(PyConfig *config)
13791427
{
13801428
wchar_t* argv[] = {
@@ -1510,6 +1558,7 @@ static struct TestCase TestCases[] = {
15101558
{"test_init_read_set", test_init_read_set},
15111559
{"test_init_run_main", test_init_run_main},
15121560
{"test_init_main", test_init_main},
1561+
{"test_init_sys_add", test_init_sys_add},
15131562
{"test_run_main", test_run_main},
15141563
{"test_open_code_hook", test_open_code_hook},
15151564
{"test_audit", test_audit},

Python/initconfig.c

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2069,6 +2069,7 @@ config_init_warnoptions(PyConfig *config,
20692069
/* The priority order for warnings configuration is (highest precedence
20702070
* first):
20712071
*
2072+
* - early PySys_AddWarnOption() calls
20722073
* - the BytesWarning filter, if needed ('-b', '-bb')
20732074
* - any '-W' command line options; then
20742075
* - the 'PYTHONWARNINGS' environment variable; then
@@ -2124,6 +2125,13 @@ config_init_warnoptions(PyConfig *config,
21242125
return status;
21252126
}
21262127
}
2128+
2129+
/* Handle early PySys_AddWarnOption() calls */
2130+
status = _PySys_ReadPreinitWarnOptions(config);
2131+
if (_PyStatus_EXCEPTION(status)) {
2132+
return status;
2133+
}
2134+
21272135
return _PyStatus_OK();
21282136
}
21292137

@@ -2293,7 +2301,8 @@ config_read_cmdline(PyConfig *config)
22932301
}
22942302

22952303
status = config_init_warnoptions(config,
2296-
&cmdline_warnoptions, &env_warnoptions);
2304+
&cmdline_warnoptions,
2305+
&env_warnoptions);
22972306
if (_PyStatus_EXCEPTION(status)) {
22982307
goto done;
22992308
}
@@ -2403,6 +2412,12 @@ PyConfig_Read(PyConfig *config)
24032412
goto done;
24042413
}
24052414

2415+
/* Handle early PySys_AddXOption() calls */
2416+
status = _PySys_ReadPreinitXOptions(config);
2417+
if (_PyStatus_EXCEPTION(status)) {
2418+
goto done;
2419+
}
2420+
24062421
status = config_read(config);
24072422
if (_PyStatus_EXCEPTION(status)) {
24082423
goto done;

Python/sysmodule.c

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2037,36 +2037,43 @@ _clear_preinit_entries(_Py_PreInitEntry *optionlist)
20372037
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
20382038
}
20392039

2040-
static void
2041-
_clear_all_preinit_options(void)
2040+
2041+
PyStatus
2042+
_PySys_ReadPreinitWarnOptions(PyConfig *config)
20422043
{
2044+
PyStatus status;
2045+
_Py_PreInitEntry entry;
2046+
2047+
for (entry = _preinit_warnoptions; entry != NULL; entry = entry->next) {
2048+
status = PyWideStringList_Append(&config->warnoptions, entry->value);
2049+
if (_PyStatus_EXCEPTION(status)) {
2050+
return status;
2051+
}
2052+
}
2053+
20432054
_clear_preinit_entries(&_preinit_warnoptions);
2044-
_clear_preinit_entries(&_preinit_xoptions);
2055+
return _PyStatus_OK();
20452056
}
20462057

2047-
static int
2048-
sys_read_preinit_options(PyThreadState *tstate)
2058+
2059+
PyStatus
2060+
_PySys_ReadPreinitXOptions(PyConfig *config)
20492061
{
2050-
/* Rerun the add commands with the actual sys module available */
2051-
if (tstate == NULL) {
2052-
/* Still don't have a thread state, so something is wrong! */
2053-
return -1;
2054-
}
2055-
_Py_PreInitEntry entry = _preinit_warnoptions;
2056-
while (entry != NULL) {
2057-
PySys_AddWarnOption(entry->value);
2058-
entry = entry->next;
2059-
}
2060-
entry = _preinit_xoptions;
2061-
while (entry != NULL) {
2062-
PySys_AddXOption(entry->value);
2063-
entry = entry->next;
2062+
PyStatus status;
2063+
_Py_PreInitEntry entry;
2064+
2065+
for (entry = _preinit_xoptions; entry != NULL; entry = entry->next) {
2066+
status = PyWideStringList_Append(&config->xoptions, entry->value);
2067+
if (_PyStatus_EXCEPTION(status)) {
2068+
return status;
2069+
}
20642070
}
20652071

2066-
_clear_all_preinit_options();
2067-
return 0;
2072+
_clear_preinit_entries(&_preinit_xoptions);
2073+
return _PyStatus_OK();
20682074
}
20692075

2076+
20702077
static PyObject *
20712078
get_warnoptions(PyThreadState *tstate)
20722079
{
@@ -2235,9 +2242,7 @@ PySys_AddXOption(const wchar_t *s)
22352242
}
22362243
if (_PySys_AddXOptionWithError(s) < 0) {
22372244
/* No return value, therefore clear error state if possible */
2238-
if (tstate) {
2239-
_PyErr_Clear(tstate);
2240-
}
2245+
_PyErr_Clear(tstate);
22412246
}
22422247
}
22432248

@@ -2898,11 +2903,6 @@ _PySys_InitMain(_PyRuntimeState *runtime, PyThreadState *tstate)
28982903
if (get_xoptions(tstate) == NULL)
28992904
return -1;
29002905

2901-
/* Transfer any sys.warnoptions and sys._xoptions set directly
2902-
* by an embedding application from the linked list to the module. */
2903-
if (sys_read_preinit_options(tstate) != 0)
2904-
return -1;
2905-
29062906
if (_PyErr_Occurred(tstate)) {
29072907
goto err_occurred;
29082908
}

0 commit comments

Comments
 (0)