Skip to content

Commit af84a88

Browse files
authored
bpo-36763: PyConfig_Read() handles PySys_AddXOption() (GH-15431) (GH-15435)
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. (cherry picked from commit 120b707)
1 parent 3921d12 commit af84a88

File tree

6 files changed

+116
-31
lines changed

6 files changed

+116
-31
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
PyInterpreterState *interp,
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
PyInterpreterState *interp);

Lib/test/test_embed.py

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

904+
def test_init_sys_add(self):
905+
config = {
906+
'faulthandler': 1,
907+
'xoptions': [
908+
'config_xoption',
909+
'cmdline_xoption',
910+
'sysadd_xoption',
911+
'faulthandler',
912+
],
913+
'warnoptions': [
914+
'ignore:::config_warnoption',
915+
'ignore:::cmdline_warnoption',
916+
'ignore:::sysadd_warnoption',
917+
],
918+
}
919+
self.check_all_configs("test_init_sys_add", config, api=API_PYTHON)
920+
904921
def test_init_run_main(self):
905922
code = ('import _testinternalcapi, json; '
906923
'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

@@ -2246,7 +2254,8 @@ config_read_cmdline(PyConfig *config)
22462254
}
22472255

22482256
status = config_init_warnoptions(config,
2249-
&cmdline_warnoptions, &env_warnoptions);
2257+
&cmdline_warnoptions,
2258+
&env_warnoptions);
22502259
if (_PyStatus_EXCEPTION(status)) {
22512260
goto done;
22522261
}
@@ -2356,6 +2365,12 @@ PyConfig_Read(PyConfig *config)
23562365
goto done;
23572366
}
23582367

2368+
/* Handle early PySys_AddXOption() calls */
2369+
status = _PySys_ReadPreinitXOptions(config);
2370+
if (_PyStatus_EXCEPTION(status)) {
2371+
goto done;
2372+
}
2373+
23592374
status = config_read(config);
23602375
if (_PyStatus_EXCEPTION(status)) {
23612376
goto done;

Python/sysmodule.c

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2071,37 +2071,43 @@ _clear_preinit_entries(_Py_PreInitEntry *optionlist)
20712071
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
20722072
}
20732073

2074-
static void
2075-
_clear_all_preinit_options(void)
2074+
2075+
PyStatus
2076+
_PySys_ReadPreinitWarnOptions(PyConfig *config)
20762077
{
2078+
PyStatus status;
2079+
_Py_PreInitEntry entry;
2080+
2081+
for (entry = _preinit_warnoptions; entry != NULL; entry = entry->next) {
2082+
status = PyWideStringList_Append(&config->warnoptions, entry->value);
2083+
if (_PyStatus_EXCEPTION(status)) {
2084+
return status;
2085+
}
2086+
}
2087+
20772088
_clear_preinit_entries(&_preinit_warnoptions);
2078-
_clear_preinit_entries(&_preinit_xoptions);
2089+
return _PyStatus_OK();
20792090
}
20802091

2081-
static int
2082-
_PySys_ReadPreInitOptions(void)
2092+
2093+
PyStatus
2094+
_PySys_ReadPreinitXOptions(PyConfig *config)
20832095
{
2084-
/* Rerun the add commands with the actual sys module available */
2085-
PyThreadState *tstate = _PyThreadState_GET();
2086-
if (tstate == NULL) {
2087-
/* Still don't have a thread state, so something is wrong! */
2088-
return -1;
2089-
}
2090-
_Py_PreInitEntry entry = _preinit_warnoptions;
2091-
while (entry != NULL) {
2092-
PySys_AddWarnOption(entry->value);
2093-
entry = entry->next;
2094-
}
2095-
entry = _preinit_xoptions;
2096-
while (entry != NULL) {
2097-
PySys_AddXOption(entry->value);
2098-
entry = entry->next;
2096+
PyStatus status;
2097+
_Py_PreInitEntry entry;
2098+
2099+
for (entry = _preinit_xoptions; entry != NULL; entry = entry->next) {
2100+
status = PyWideStringList_Append(&config->xoptions, entry->value);
2101+
if (_PyStatus_EXCEPTION(status)) {
2102+
return status;
2103+
}
20992104
}
21002105

2101-
_clear_all_preinit_options();
2102-
return 0;
2106+
_clear_preinit_entries(&_preinit_xoptions);
2107+
return _PyStatus_OK();
21032108
}
21042109

2110+
21052111
static PyObject *
21062112
get_warnoptions(void)
21072113
{
@@ -2265,9 +2271,7 @@ PySys_AddXOption(const wchar_t *s)
22652271
}
22662272
if (_PySys_AddXOptionWithError(s) < 0) {
22672273
/* No return value, therefore clear error state if possible */
2268-
if (_PyThreadState_UncheckedGet()) {
2269-
PyErr_Clear();
2270-
}
2274+
PyErr_Clear();
22712275
}
22722276
}
22732277

@@ -2926,13 +2930,9 @@ _PySys_InitMain(_PyRuntimeState *runtime, PyInterpreterState *interp)
29262930
if (get_xoptions() == NULL)
29272931
return -1;
29282932

2929-
/* Transfer any sys.warnoptions and sys._xoptions set directly
2930-
* by an embedding application from the linked list to the module. */
2931-
if (_PySys_ReadPreInitOptions() != 0)
2932-
return -1;
2933-
29342933
if (PyErr_Occurred())
29352934
return -1;
2935+
29362936
return 0;
29372937

29382938
err_occurred:

0 commit comments

Comments
 (0)