Skip to content

Commit

Permalink
New PyConfig_Get() API
Browse files Browse the repository at this point in the history
  • Loading branch information
vstinner committed Dec 1, 2023
1 parent 328fa6d commit fee8cc4
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 123 deletions.
18 changes: 4 additions & 14 deletions Include/initconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,20 +98,10 @@ PyAPI_FUNC(int) PyConfig_GetInt(
const char *name,
int64_t *value);

// Get a string configuration option.
// Return 0 and set '*value' on success. '*value' can be set to a Python str
// object or to None.
// Raise an exception return -1 on error.
PyAPI_FUNC(int) PyConfig_GetStr(
const char *name,
PyObject **value);

// Get a string configuration option.
// Return 0 and set '*value' to a Python list on success.
// Raise an exception return -1 on error.
PyAPI_FUNC(int) PyConfig_GetStrList(
const char *name,
PyObject **value);
// Get a configuration option.
// Return a new reference on success.
// Set an exception and return NULL on error.
PyAPI_FUNC(PyObject*) PyConfig_Get(const char *name);

#endif // !Py_LIMITED_API

Expand Down
28 changes: 11 additions & 17 deletions Lib/test/test_capi/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,12 @@ def test_config_getint(self):
self.assertEqual(config_getint('write_bytecode'), 1)

def test_config_getstr(self):
config_getstr = _testcapi.config_getstr
config_get = _testcapi.config_get
# PyConfig_MEMBER_WSTR type
self.assertEqual(config_getstr('platlibdir'), sys.platlibdir)
self.assertEqual(config_get('platlibdir'), sys.platlibdir)
if 'PYTHONDUMPREFSFILE' not in os.environ:
# PyConfig_MEMBER_WSTR_OPT type
self.assertIsNone(config_getstr('dump_refs_file'))

# verbose is an int
self.check_error(config_getstr, 'verbose')
self.assertIsNone(config_get('dump_refs_file'))

# attributes read from sys
value = "TEST_MARKER_STR"
Expand All @@ -69,7 +66,7 @@ def test_config_getstr(self):
):
with self.subTest(name=name):
with support.swap_attr(sys, name, value):
self.assertEqual(config_getstr(name), value)
self.assertEqual(config_get(name), value)

# attributes read from sys with a different name
# (add underscore prefix)
Expand All @@ -79,14 +76,11 @@ def test_config_getstr(self):
):
with self.subTest(config_name=config_name, sys_name=sys_name):
with support.swap_attr(sys, sys_name, value):
self.assertEqual(config_getstr(config_name), value)
self.assertEqual(config_get(config_name), value)

def test_config_getstrlist(self):
config_getstrlist = _testcapi.config_getstrlist
self.assertEqual(config_getstrlist('orig_argv'), sys.orig_argv)

# verbose is an int
self.check_error(config_getstrlist, 'verbose')
config_get = _testcapi.config_get
self.assertEqual(config_get('orig_argv'), sys.orig_argv)

# attributes read from sys
value = ["TEST_MARKER_STRLIST"]
Expand All @@ -97,19 +91,19 @@ def test_config_getstrlist(self):
):
with self.subTest(name=name):
with support.swap_attr(sys, name, value):
self.assertEqual(config_getstrlist(name), value)
self.assertEqual(config_get(name), value)

with support.swap_attr(sys, "path", value):
self.assertEqual(config_getstrlist("module_search_paths"), value)
self.assertEqual(config_get("module_search_paths"), value)

with support.swap_attr(sys, "_xoptions", {"x": "value", "y": True}):
self.assertEqual(config_getstrlist("xoptions"),
self.assertEqual(config_get("xoptions"),
["x=value", "y"])

# sys._xoptions must be a dict
with support.swap_attr(sys, "_xoptions", "not_a_dict"):
with self.assertRaises(TypeError):
config_getstrlist("xoptions")
config_get("xoptions")


if __name__ == "__main__":
Expand Down
27 changes: 3 additions & 24 deletions Modules/_testcapi/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,41 +26,20 @@ _testcapi_config_getint(PyObject *module, PyObject *name_obj)


static PyObject *
_testcapi_config_getstr(PyObject *module, PyObject *name_obj)
_testcapi_config_get(PyObject *module, PyObject *name_obj)
{
const char *name;
if (PyArg_Parse(name_obj, "s", &name) < 0) {
return NULL;
}

PyObject *value;
if (PyConfig_GetStr(name, &value) < 0) {
return NULL;
}
return value;
}


static PyObject *
_testcapi_config_getstrlist(PyObject *module, PyObject *name_obj)
{
const char *name;
if (PyArg_Parse(name_obj, "s", &name) < 0) {
return NULL;
}

PyObject *value;
if (PyConfig_GetStrList(name, &value) < 0) {
return NULL;
}
return value;
return PyConfig_Get(name);
}


static PyMethodDef test_methods[] = {
{"config_getint", _testcapi_config_getint, METH_O},
{"config_getstr", _testcapi_config_getstr, METH_O},
{"config_getstrlist", _testcapi_config_getstrlist, METH_O},
{"config_get", _testcapi_config_get, METH_O},
{NULL}
};

Expand Down
157 changes: 89 additions & 68 deletions Python/initconfig.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ config_get_int(const PyConfig *config, const PyConfigSpec *spec,
static int
config_get_str(const PyConfig *config, const PyConfigSpec *spec,
PyObject **value, int use_sys);
static int
config_get_str_list(const PyConfig *config, const PyConfigSpec *spec,
PyObject **value, int use_sys);
static PyObject*
config_get(const PyConfig *config, const PyConfigSpec *spec,
int use_sys);

/* --- PyConfig spec ---------------------------------------------- */

Expand Down Expand Up @@ -1061,47 +1061,6 @@ _PyConfig_Copy(PyConfig *config, const PyConfig *config2)
}


static PyObject*
_PyConfig_Get(const PyConfig *config, const PyConfigSpec *spec)
{
switch (spec->type) {
case PyConfig_MEMBER_INT:
case PyConfig_MEMBER_UINT:
case PyConfig_MEMBER_ULONG:
{
int64_t value;
if (config_get_int(config, spec, &value, 0) < 0) {
return NULL;
}

Py_BUILD_ASSERT(sizeof(value) == sizeof(long long));
long long llvalue = (long long)value;
return PyLong_FromLongLong(llvalue);
}
case PyConfig_MEMBER_WSTR:
case PyConfig_MEMBER_WSTR_OPT:
{
PyObject *obj;
if (config_get_str(config, spec, &obj, 0) < 0) {
return NULL;
}
return obj;
}
case PyConfig_MEMBER_WSTR_LIST:
{
PyObject *obj;
if (config_get_str_list(config, spec, &obj, 0) < 0) {
return NULL;
}
return obj;
}
default:
break;
}
Py_UNREACHABLE();
}


PyObject *
_PyConfig_AsDict(const PyConfig *config)
{
Expand All @@ -1112,7 +1071,7 @@ _PyConfig_AsDict(const PyConfig *config)

const PyConfigSpec *spec = PYCONFIG_SPEC;
for (; spec->name != NULL; spec++) {
PyObject *obj = _PyConfig_Get(config, spec);
PyObject *obj = config_get(config, spec, 0);
if (obj == NULL) {
Py_DECREF(dict);
return NULL;
Expand Down Expand Up @@ -3672,7 +3631,6 @@ config_get_str(const PyConfig *config, const PyConfigSpec *spec,
}
}

wchar_t **member = config_spec_get_member(spec, config);
if (spec->type != PyConfig_MEMBER_WSTR
&& spec->type != PyConfig_MEMBER_WSTR_OPT)
{
Expand All @@ -3681,6 +3639,7 @@ config_get_str(const PyConfig *config, const PyConfigSpec *spec,
return -1;
}

wchar_t **member = config_spec_get_member(spec, config);
if (*member != NULL) {
*value = PyUnicode_FromWideChar(*member, -1);
if (*value == NULL) {
Expand Down Expand Up @@ -3745,13 +3704,25 @@ config_dict_as_str_list(PyObject *dict)
}


static int
config_get_str_list(const PyConfig *config, const PyConfigSpec *spec,
PyObject **value, int use_sys)
static PyObject*
config_get(const PyConfig *config, const PyConfigSpec *spec,
int use_sys)
{
if (use_sys && !_PyRuntime.initialized) {
use_sys = 0;
}
if (use_sys) {
if (strcmp(spec->name, "write_bytecode") == 0) {
PyObject *attr = PySys_GetObject("dont_write_bytecode");
if (attr != NULL) {
int is_true = PyObject_IsTrue(attr);
if (is_true < 0) {
return NULL;
}
return PyLong_FromLong(!is_true);
}
}
}
if (use_sys) {
const char* sys_attrs[] = {
"argv",
Expand All @@ -3765,43 +3736,93 @@ config_get_str_list(const PyConfig *config, const PyConfigSpec *spec,
for (const char **attr = sys_attrs; *attr != NULL; attr++) {
if (strcmp(name, *attr) == 0) {
if (strcmp(name, "module_search_paths") == 0) {
*value = Py_XNewRef(PySys_GetObject("path"));
return Py_XNewRef(PySys_GetObject("path"));
}
else if (strcmp(name, "xoptions") == 0) {
*value = config_dict_as_str_list(PySys_GetObject("_xoptions"));
return config_dict_as_str_list(PySys_GetObject("_xoptions"));
}
else {
*value = Py_XNewRef(PySys_GetObject(name));
return Py_XNewRef(PySys_GetObject(name));
}
if (*value == NULL) {
return -1;
}
}
}
if (use_sys) {
const char* sys_attrs[] = {
"base_exec_prefix",
"base_executable",
"base_prefix",
"exec_prefix",
"executable",
"platlibdir",
"prefix",
"pycache_prefix",
"stdlib_dir",
NULL,
};
const char *name = spec->name;
for (const char **attr = sys_attrs; *attr != NULL; attr++) {
if (strcmp(name, *attr) == 0) {
if (strcmp(name, "stdlib_dir") == 0) {
return Py_XNewRef(PySys_GetObject("_stdlib_dir"));
}
else if (strcmp(name, "base_executable") == 0) {
return Py_XNewRef(PySys_GetObject("_base_executable"));
}
else {
return Py_XNewRef(PySys_GetObject(name));
}
return 0;
}
}
}

if (spec->type != PyConfig_MEMBER_WSTR_LIST) {
PyErr_Format(PyExc_TypeError,
"config option %s is not a strings list", spec->name);
return -1;
char *member = config_spec_get_member(spec, config);
switch (spec->type) {
case PyConfig_MEMBER_INT:
case PyConfig_MEMBER_UINT:
{
int value = *(int *)member;
return PyLong_FromLong(value);
}

const PyWideStringList *list = config_spec_get_member(spec, config);
*value = _PyWideStringList_AsList(list);
if (*value == NULL) {
return -1;
case PyConfig_MEMBER_ULONG:
{
unsigned long value = *(unsigned long *)member;
return PyLong_FromUnsignedLong(value);
}

case PyConfig_MEMBER_WSTR:
case PyConfig_MEMBER_WSTR_OPT:
{
wchar_t *wstr = *(wchar_t **)member;
if (wstr != NULL) {
return PyUnicode_FromWideChar(wstr, -1);
}
else {
return Py_NewRef(Py_None);
}
}

case PyConfig_MEMBER_WSTR_LIST:
{
const PyWideStringList *list = (const PyWideStringList *)member;
return _PyWideStringList_AsList(list);
}
default:
PyErr_Format(PyExc_TypeError,
"config option %s is not a strings list", spec->name);
return NULL;
}
return 0;
}

int
PyConfig_GetStrList(const char *name, PyObject **value)

PyObject*
PyConfig_Get(const char *name)
{
const PyConfigSpec *spec = config_prepare_get(name);
if (spec == NULL) {
return -1;
return NULL;
}
const PyConfig *config = _Py_GetConfig();
return config_get_str_list(config, spec, value, 1);
return config_get(config, spec, 1);
}

0 comments on commit fee8cc4

Please sign in to comment.