Skip to content

Commit 96cb693

Browse files
committed
pythongh-107954: Add _PyConfig_Parse()
1 parent 89966a6 commit 96cb693

File tree

5 files changed

+602
-285
lines changed

5 files changed

+602
-285
lines changed

Include/internal/pycore_initconfig.h

+6
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,12 @@ PyAPI_FUNC(int) _PyConfig_FromDict(PyConfig *config, PyObject *dict);
182182
PyAPI_FUNC(PyObject*) _Py_Get_Getpath_CodeObject(void);
183183
PyAPI_FUNC(PyObject*) _Py_GetConfigsAsDict(void);
184184

185+
PyAPI_FUNC(int) _PyConfig_Parse(
186+
PyObject *config_dict,
187+
// UTF-8 encoded string
188+
const char *config_str
189+
);
190+
185191
#ifdef __cplusplus
186192
}
187193
#endif

Lib/test/test_initconfig.py

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import textwrap
2+
import unittest
3+
from test.support import import_helper
4+
5+
_testinternalcapi = import_helper.import_module('_testinternalcapi')
6+
7+
8+
class InitConfigTests(unittest.TestCase):
9+
def check_parse(self, text, expected):
10+
text = textwrap.dedent(text)
11+
config = _testinternalcapi.parse_config_text(text)
12+
self.assertEqual(config, expected)
13+
14+
def test_parse(self):
15+
self.check_parse('verbose=1', {'verbose': 1})
16+
17+
# strip spaces
18+
self.check_parse(' \tverbose = 1 ', {'verbose': 1})
19+
20+
# comments
21+
self.check_parse('''
22+
verbose=1
23+
# ignored comment
24+
verbose=2 # more #hashtag comment
25+
''', dict(
26+
verbose = 2,
27+
))
28+
self.check_parse('verbose=1', {'verbose': 1})
29+
30+
# types
31+
self.check_parse('''
32+
# int
33+
int_max_str_digits = -123
34+
# uint
35+
isolated = 7
36+
# ulong
37+
hash_seed = 555
38+
# wstr
39+
filesystem_encoding = with spaces
40+
# wstr optional
41+
pycache_prefix =
42+
# wstr list
43+
argv = 1, 2, 3
44+
''', dict(
45+
int_max_str_digits = -123,
46+
isolated = 7,
47+
hash_seed = 555,
48+
filesystem_encoding = "with spaces",
49+
pycache_prefix = None,
50+
argv = "1, 2, 3",
51+
))
52+
53+
54+
if __name__ == "__main__":
55+
unittest.main()

Modules/_testinternalcapi.c

+73
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,77 @@ test_set_config(PyObject *Py_UNUSED(self), PyObject *dict)
341341
}
342342

343343

344+
static PyObject *
345+
test_parse_config_text(PyObject *Py_UNUSED(self), PyObject *config_obj)
346+
{
347+
const char *config_utf8 = PyUnicode_AsUTF8(config_obj);
348+
if (config_utf8 == NULL) {
349+
return NULL;
350+
}
351+
352+
PyObject *config_dict = PyDict_New();
353+
if (config_dict == NULL) {
354+
return NULL;
355+
}
356+
357+
if (_PyConfig_Parse(config_dict, config_utf8) < 0) {
358+
Py_DECREF(config_dict);
359+
return NULL;
360+
}
361+
return config_dict;
362+
}
363+
364+
365+
static PyObject *
366+
test_set_config_text(PyObject *Py_UNUSED(self), PyObject *config_obj)
367+
{
368+
if (!PyUnicode_Check(config_obj)) {
369+
PyErr_Format(PyExc_TypeError, "expected str, got %s",
370+
Py_TYPE(config_obj)->tp_name);
371+
return NULL;
372+
}
373+
374+
PyConfig config;
375+
PyConfig_InitIsolatedConfig(&config);
376+
PyObject *config_dict = NULL;
377+
378+
// Set config from the dict
379+
if (_PyInterpreterState_GetConfigCopy(&config) < 0) {
380+
goto error;
381+
}
382+
config_dict = _PyConfig_AsDict(&config);
383+
if (config_dict == NULL) {
384+
goto error;
385+
}
386+
387+
// Parse config text
388+
const char *config_utf8 = PyUnicode_AsUTF8(config_obj);
389+
if (config_utf8 == NULL) {
390+
goto error;
391+
}
392+
if (_PyConfig_Parse(config_dict, config_utf8) < 0) {
393+
goto error;
394+
}
395+
396+
// Set config from dict
397+
if (_PyConfig_FromDict(&config, config_dict) < 0) {
398+
goto error;
399+
}
400+
if (_PyInterpreterState_SetConfig(&config) < 0) {
401+
goto error;
402+
}
403+
404+
PyConfig_Clear(&config);
405+
Py_DECREF(config_dict);
406+
Py_RETURN_NONE;
407+
408+
error:
409+
PyConfig_Clear(&config);
410+
Py_DECREF(config_dict);
411+
return NULL;
412+
}
413+
414+
344415
static PyObject *
345416
test_reset_path_config(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(arg))
346417
{
@@ -1536,6 +1607,8 @@ static PyMethodDef module_functions[] = {
15361607
{"test_hashtable", test_hashtable, METH_NOARGS},
15371608
{"get_config", test_get_config, METH_NOARGS},
15381609
{"set_config", test_set_config, METH_O},
1610+
{"parse_config_text", test_parse_config_text, METH_O},
1611+
{"set_config_text", test_set_config_text, METH_O},
15391612
{"reset_path_config", test_reset_path_config, METH_NOARGS},
15401613
{"test_edit_cost", test_edit_cost, METH_NOARGS},
15411614
{"test_bytes_find", test_bytes_find, METH_NOARGS},

0 commit comments

Comments
 (0)