Skip to content

Commit 818a6c9

Browse files
committed
Add PyConfig_Get()
1 parent 79404e9 commit 818a6c9

File tree

4 files changed

+302
-2
lines changed

4 files changed

+302
-2
lines changed

docs/api.rst

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,11 +177,17 @@ Python 3.14
177177

178178
See `Py_fclose() documentation <https://docs.python.org/dev/c-api/sys.html#c.Py_fclose>`__.
179179

180+
.. c:function:: PyObject* PyConfig_Get(const char *name)
181+
182+
See `PyConfig_Get() documentation <https://docs.python.org/dev/c-api/init_config.html#c.PyConfig_Get>`__.
183+
184+
.. c:function:: int PyConfig_GetInt(const char *name, int *value)
185+
186+
See `PyConfig_GetInt() documentation <https://docs.python.org/dev/c-api/init_config.html#c.PyConfig_GetInt>`__.
187+
180188

181189
Not supported:
182190

183-
* ``PyConfig_Get()``
184-
* ``PyConfig_GetInt()``
185191
* ``PyConfig_Names()``
186192
* ``PyConfig_Set()``
187193
* ``PyInitConfig_AddModule()``

docs/changelog.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
Changelog
22
=========
33

4+
* 2025-01-19: Add ``PyConfig_Get()`` functions.
45
* 2025-01-06: Add ``Py_fopen()`` and ``Py_fclose()`` functions.
56
* 2024-12-16: Add ``structmember.h`` constants:
67

pythoncapi_compat.h

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ extern "C" {
1919
#endif
2020

2121
#include <Python.h>
22+
#include <stddef.h> // offsetof()
2223

2324
// Python 3.11.0b4 added PyFrame_Back() to Python.h
2425
#if PY_VERSION_HEX < 0x030b00B4 && !defined(PYPY_VERSION)
@@ -1974,6 +1975,235 @@ int Py_fclose(FILE *file)
19741975
#endif
19751976

19761977

1978+
#if 0x03090000 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x030E0000 && !defined(PYPY_VERSION)
1979+
static inline PyObject*
1980+
PyConfig_Get(const char *name)
1981+
{
1982+
typedef enum {
1983+
_PyConfig_MEMBER_INT,
1984+
_PyConfig_MEMBER_UINT,
1985+
_PyConfig_MEMBER_ULONG,
1986+
_PyConfig_MEMBER_BOOL,
1987+
_PyConfig_MEMBER_WSTR,
1988+
_PyConfig_MEMBER_WSTR_OPT,
1989+
_PyConfig_MEMBER_WSTR_LIST,
1990+
} PyConfigMemberType;
1991+
1992+
typedef struct {
1993+
const char *name;
1994+
size_t offset;
1995+
PyConfigMemberType type;
1996+
const char *sys_attr;
1997+
} PyConfigSpec;
1998+
1999+
#define PYTHONCAPI_COMPAT_SPEC(MEMBER, TYPE, sys_attr) \
2000+
{#MEMBER, offsetof(PyConfig, MEMBER), \
2001+
_PyConfig_MEMBER_##TYPE, sys_attr}
2002+
2003+
static const PyConfigSpec config_spec[] = {
2004+
PYTHONCAPI_COMPAT_SPEC(argv, WSTR_LIST, "argv"),
2005+
PYTHONCAPI_COMPAT_SPEC(base_exec_prefix, WSTR_OPT, "base_exec_prefix"),
2006+
PYTHONCAPI_COMPAT_SPEC(base_executable, WSTR_OPT, "_base_executable"),
2007+
PYTHONCAPI_COMPAT_SPEC(base_prefix, WSTR_OPT, "base_prefix"),
2008+
PYTHONCAPI_COMPAT_SPEC(bytes_warning, UINT, _Py_NULL),
2009+
PYTHONCAPI_COMPAT_SPEC(exec_prefix, WSTR_OPT, "exec_prefix"),
2010+
PYTHONCAPI_COMPAT_SPEC(executable, WSTR_OPT, "executable"),
2011+
PYTHONCAPI_COMPAT_SPEC(inspect, BOOL, _Py_NULL),
2012+
#if 0x030C0000 <= PY_VERSION_HEX
2013+
PYTHONCAPI_COMPAT_SPEC(int_max_str_digits, UINT, _Py_NULL),
2014+
#endif
2015+
PYTHONCAPI_COMPAT_SPEC(interactive, BOOL, _Py_NULL),
2016+
PYTHONCAPI_COMPAT_SPEC(module_search_paths, WSTR_LIST, "path"),
2017+
PYTHONCAPI_COMPAT_SPEC(optimization_level, UINT, _Py_NULL),
2018+
PYTHONCAPI_COMPAT_SPEC(parser_debug, BOOL, _Py_NULL),
2019+
PYTHONCAPI_COMPAT_SPEC(platlibdir, WSTR, "platlibdir"),
2020+
PYTHONCAPI_COMPAT_SPEC(prefix, WSTR_OPT, "prefix"),
2021+
PYTHONCAPI_COMPAT_SPEC(pycache_prefix, WSTR_OPT, "pycache_prefix"),
2022+
PYTHONCAPI_COMPAT_SPEC(quiet, BOOL, _Py_NULL),
2023+
#if 0x030B0000 <= PY_VERSION_HEX
2024+
PYTHONCAPI_COMPAT_SPEC(stdlib_dir, WSTR_OPT, "_stdlib_dir"),
2025+
#endif
2026+
PYTHONCAPI_COMPAT_SPEC(use_environment, BOOL, _Py_NULL),
2027+
PYTHONCAPI_COMPAT_SPEC(verbose, UINT, _Py_NULL),
2028+
PYTHONCAPI_COMPAT_SPEC(warnoptions, WSTR_LIST, "warnoptions"),
2029+
PYTHONCAPI_COMPAT_SPEC(write_bytecode, BOOL, _Py_NULL),
2030+
PYTHONCAPI_COMPAT_SPEC(xoptions, WSTR_LIST, "_xoptions"),
2031+
#ifdef Py_STATS
2032+
PYTHONCAPI_COMPAT_SPEC(_pystats, BOOL, _Py_NULL),
2033+
#endif
2034+
PYTHONCAPI_COMPAT_SPEC(buffered_stdio, BOOL, _Py_NULL),
2035+
PYTHONCAPI_COMPAT_SPEC(check_hash_pycs_mode, WSTR, _Py_NULL),
2036+
#if 0x030B0000 <= PY_VERSION_HEX
2037+
PYTHONCAPI_COMPAT_SPEC(code_debug_ranges, BOOL, _Py_NULL),
2038+
#endif
2039+
PYTHONCAPI_COMPAT_SPEC(configure_c_stdio, BOOL, _Py_NULL),
2040+
#if 0x030D0000 <= PY_VERSION_HEX
2041+
PYTHONCAPI_COMPAT_SPEC(cpu_count, INT, _Py_NULL),
2042+
#endif
2043+
PYTHONCAPI_COMPAT_SPEC(dev_mode, BOOL, _Py_NULL),
2044+
PYTHONCAPI_COMPAT_SPEC(dump_refs, BOOL, _Py_NULL),
2045+
#if 0x030B0000 <= PY_VERSION_HEX
2046+
PYTHONCAPI_COMPAT_SPEC(dump_refs_file, WSTR_OPT, _Py_NULL),
2047+
#endif
2048+
#ifdef Py_GIL_DISABLED
2049+
PYTHONCAPI_COMPAT_SPEC(enable_gil, INT, _Py_NULL),
2050+
#endif
2051+
PYTHONCAPI_COMPAT_SPEC(faulthandler, BOOL, _Py_NULL),
2052+
PYTHONCAPI_COMPAT_SPEC(filesystem_encoding, WSTR, _Py_NULL),
2053+
PYTHONCAPI_COMPAT_SPEC(filesystem_errors, WSTR, _Py_NULL),
2054+
PYTHONCAPI_COMPAT_SPEC(hash_seed, ULONG, _Py_NULL),
2055+
PYTHONCAPI_COMPAT_SPEC(home, WSTR_OPT, _Py_NULL),
2056+
PYTHONCAPI_COMPAT_SPEC(import_time, BOOL, _Py_NULL),
2057+
PYTHONCAPI_COMPAT_SPEC(install_signal_handlers, BOOL, _Py_NULL),
2058+
PYTHONCAPI_COMPAT_SPEC(isolated, BOOL, _Py_NULL),
2059+
#ifdef MS_WINDOWS
2060+
PYTHONCAPI_COMPAT_SPEC(legacy_windows_stdio, BOOL, _Py_NULL),
2061+
#endif
2062+
PYTHONCAPI_COMPAT_SPEC(malloc_stats, BOOL, _Py_NULL),
2063+
#if 0x030A0000 <= PY_VERSION_HEX
2064+
PYTHONCAPI_COMPAT_SPEC(orig_argv, WSTR_LIST, "orig_argv"),
2065+
#endif
2066+
PYTHONCAPI_COMPAT_SPEC(parse_argv, BOOL, _Py_NULL),
2067+
PYTHONCAPI_COMPAT_SPEC(pathconfig_warnings, BOOL, _Py_NULL),
2068+
#if 0x030C0000 <= PY_VERSION_HEX
2069+
PYTHONCAPI_COMPAT_SPEC(perf_profiling, UINT, _Py_NULL),
2070+
#endif
2071+
PYTHONCAPI_COMPAT_SPEC(program_name, WSTR, _Py_NULL),
2072+
PYTHONCAPI_COMPAT_SPEC(run_command, WSTR_OPT, _Py_NULL),
2073+
PYTHONCAPI_COMPAT_SPEC(run_filename, WSTR_OPT, _Py_NULL),
2074+
PYTHONCAPI_COMPAT_SPEC(run_module, WSTR_OPT, _Py_NULL),
2075+
#ifdef Py_DEBUG
2076+
PYTHONCAPI_COMPAT_SPEC(run_presite, WSTR_OPT, _Py_NULL),
2077+
#endif
2078+
#if 0x030B0000 <= PY_VERSION_HEX
2079+
PYTHONCAPI_COMPAT_SPEC(safe_path, BOOL, _Py_NULL),
2080+
#endif
2081+
PYTHONCAPI_COMPAT_SPEC(show_ref_count, BOOL, _Py_NULL),
2082+
PYTHONCAPI_COMPAT_SPEC(site_import, BOOL, _Py_NULL),
2083+
PYTHONCAPI_COMPAT_SPEC(skip_source_first_line, BOOL, _Py_NULL),
2084+
PYTHONCAPI_COMPAT_SPEC(stdio_encoding, WSTR, _Py_NULL),
2085+
PYTHONCAPI_COMPAT_SPEC(stdio_errors, WSTR, _Py_NULL),
2086+
PYTHONCAPI_COMPAT_SPEC(tracemalloc, UINT, _Py_NULL),
2087+
#if 0x030B0000 <= PY_VERSION_HEX
2088+
PYTHONCAPI_COMPAT_SPEC(use_frozen_modules, BOOL, _Py_NULL),
2089+
#endif
2090+
PYTHONCAPI_COMPAT_SPEC(use_hash_seed, BOOL, _Py_NULL),
2091+
#ifdef __APPLE__
2092+
PYTHONCAPI_COMPAT_SPEC(use_system_logger, BOOL, _Py_NULL),
2093+
#endif
2094+
PYTHONCAPI_COMPAT_SPEC(user_site_directory, BOOL, _Py_NULL),
2095+
#if 0x030A0000 <= PY_VERSION_HEX
2096+
PYTHONCAPI_COMPAT_SPEC(warn_default_encoding, BOOL, _Py_NULL),
2097+
#endif
2098+
};
2099+
2100+
#undef PYTHONCAPI_COMPAT_SPEC
2101+
2102+
const PyConfigSpec *spec;
2103+
int found = 0;
2104+
for (size_t i=0; i < Py_ARRAY_LENGTH(config_spec); i++) {
2105+
spec = &config_spec[i];
2106+
if (strcmp(spec->name, name) == 0) {
2107+
found = 1;
2108+
break;
2109+
}
2110+
}
2111+
if (found) {
2112+
if (spec->sys_attr != NULL) {
2113+
PyObject *value = PySys_GetObject(spec->sys_attr);
2114+
if (value == NULL) {
2115+
PyErr_Format(PyExc_RuntimeError, "lost sys.%s", spec->sys_attr);
2116+
return NULL;
2117+
}
2118+
return Py_NewRef(value);
2119+
}
2120+
2121+
extern const PyConfig* _Py_GetConfig(void);
2122+
const PyConfig *config = _Py_GetConfig();
2123+
void *member = (char *)config + spec->offset;
2124+
switch (spec->type) {
2125+
case _PyConfig_MEMBER_INT:
2126+
case _PyConfig_MEMBER_UINT:
2127+
{
2128+
int value = *(int *)member;
2129+
return PyLong_FromLong(value);
2130+
}
2131+
case _PyConfig_MEMBER_BOOL:
2132+
{
2133+
int value = *(int *)member;
2134+
return PyBool_FromLong(value != 0);
2135+
}
2136+
case _PyConfig_MEMBER_ULONG:
2137+
{
2138+
unsigned long value = *(unsigned long *)member;
2139+
return PyLong_FromUnsignedLong(value);
2140+
}
2141+
case _PyConfig_MEMBER_WSTR:
2142+
case _PyConfig_MEMBER_WSTR_OPT:
2143+
{
2144+
wchar_t *wstr = *(wchar_t **)member;
2145+
if (wstr != NULL) {
2146+
return PyUnicode_FromWideChar(wstr, -1);
2147+
}
2148+
else {
2149+
return Py_NewRef(Py_None);
2150+
}
2151+
}
2152+
case _PyConfig_MEMBER_WSTR_LIST:
2153+
{
2154+
const PyWideStringList *list = (const PyWideStringList *)member;
2155+
PyObject *tuple = PyTuple_New(list->length);
2156+
if (tuple == NULL) {
2157+
return NULL;
2158+
}
2159+
2160+
for (Py_ssize_t i = 0; i < list->length; i++) {
2161+
PyObject *item = PyUnicode_FromWideChar(list->items[i], -1);
2162+
if (item == NULL) {
2163+
Py_DECREF(tuple);
2164+
return NULL;
2165+
}
2166+
PyTuple_SET_ITEM(tuple, i, item);
2167+
}
2168+
return tuple;
2169+
}
2170+
default:
2171+
Py_UNREACHABLE();
2172+
}
2173+
}
2174+
2175+
PyErr_Format(PyExc_ValueError, "unknown config option name: %s", name);
2176+
return NULL;
2177+
}
2178+
2179+
static inline int
2180+
PyConfig_GetInt(const char *name, int *value)
2181+
{
2182+
PyObject *obj = PyConfig_Get(name);
2183+
if (obj == NULL) {
2184+
return -1;
2185+
}
2186+
2187+
if (!PyLong_Check(obj)) {
2188+
Py_DECREF(obj);
2189+
PyErr_Format(PyExc_TypeError, "config option %s is not an int", name);
2190+
return -1;
2191+
}
2192+
2193+
int as_int = PyLong_AsInt(obj);
2194+
Py_DECREF(obj);
2195+
if (as_int == -1 && PyErr_Occurred()) {
2196+
PyErr_Format(PyExc_OverflowError,
2197+
"config option %s value does not fit into a C int", name);
2198+
return -1;
2199+
}
2200+
2201+
*value = as_int;
2202+
return 0;
2203+
}
2204+
#endif // PY_VERSION_HEX > 0x03090000 && !defined(PYPY_VERSION)
2205+
2206+
19772207
#ifdef __cplusplus
19782208
}
19792209
#endif

tests/test_pythoncapi_compat_cext.c

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2112,6 +2112,66 @@ test_file(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
21122112
}
21132113

21142114

2115+
#if 0x03090000 <= PY_VERSION_HEX && !defined(PYPY_VERSION)
2116+
static PyObject *
2117+
test_config(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
2118+
{
2119+
// Test PyConfig_Get()
2120+
PyObject *sys = PyImport_ImportModule("sys");
2121+
if (sys == _Py_NULL) {
2122+
return _Py_NULL;
2123+
}
2124+
2125+
PyObject *obj = PyConfig_Get("argv");
2126+
PyObject *sys_attr = PyObject_GetAttrString(sys, "argv");
2127+
assert(obj == sys_attr);
2128+
Py_DECREF(obj);
2129+
Py_DECREF(sys_attr);
2130+
2131+
obj = PyConfig_Get("module_search_paths");
2132+
sys_attr = PyObject_GetAttrString(sys, "path");
2133+
assert(obj == sys_attr);
2134+
Py_DECREF(obj);
2135+
Py_DECREF(sys_attr);
2136+
2137+
obj = PyConfig_Get("xoptions");
2138+
sys_attr = PyObject_GetAttrString(sys, "_xoptions");
2139+
assert(obj == sys_attr);
2140+
Py_DECREF(obj);
2141+
Py_DECREF(sys_attr);
2142+
2143+
obj = PyConfig_Get("use_environment");
2144+
assert(PyBool_Check(obj));
2145+
Py_DECREF(obj);
2146+
2147+
obj = PyConfig_Get("verbose");
2148+
assert(PyLong_Check(obj));
2149+
Py_DECREF(obj);
2150+
2151+
assert(PyConfig_Get("nonexistent") == NULL);
2152+
assert(PyErr_ExceptionMatches(PyExc_ValueError));
2153+
PyErr_Clear();
2154+
2155+
// Test PyConfig_GetInt()
2156+
int value = -3;
2157+
2158+
assert(PyConfig_GetInt("verbose", &value) == 0);
2159+
assert(value >= 0);
2160+
2161+
assert(PyConfig_GetInt("argv", &value) == -1);
2162+
assert(PyErr_ExceptionMatches(PyExc_TypeError));
2163+
PyErr_Clear();
2164+
2165+
assert(PyConfig_GetInt("nonexistent", &value) == -1);
2166+
assert(PyErr_ExceptionMatches(PyExc_ValueError));
2167+
PyErr_Clear();
2168+
2169+
Py_DECREF(sys);
2170+
Py_RETURN_NONE;
2171+
}
2172+
#endif
2173+
2174+
21152175
static struct PyMethodDef methods[] = {
21162176
{"test_object", test_object, METH_NOARGS, _Py_NULL},
21172177
{"test_py_is", test_py_is, METH_NOARGS, _Py_NULL},
@@ -2160,6 +2220,9 @@ static struct PyMethodDef methods[] = {
21602220
{"test_long_stdint", test_long_stdint, METH_NOARGS, _Py_NULL},
21612221
{"test_structmember", test_structmember, METH_NOARGS, _Py_NULL},
21622222
{"test_file", test_file, METH_NOARGS, _Py_NULL},
2223+
#if 0x03090000 <= PY_VERSION_HEX && !defined(PYPY_VERSION)
2224+
{"test_config", test_config, METH_NOARGS, _Py_NULL},
2225+
#endif
21632226
{_Py_NULL, _Py_NULL, 0, _Py_NULL}
21642227
};
21652228

0 commit comments

Comments
 (0)