Skip to content

Commit 8a52253

Browse files
authored
Add PyConfig_Get() (#128)
1 parent b1b2071 commit 8a52253

File tree

4 files changed

+296
-2
lines changed

4 files changed

+296
-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: 224 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,229 @@ 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+
PYTHONCAPI_COMPAT_SPEC(buffered_stdio, BOOL, _Py_NULL),
2032+
PYTHONCAPI_COMPAT_SPEC(check_hash_pycs_mode, WSTR, _Py_NULL),
2033+
#if 0x030B0000 <= PY_VERSION_HEX
2034+
PYTHONCAPI_COMPAT_SPEC(code_debug_ranges, BOOL, _Py_NULL),
2035+
#endif
2036+
PYTHONCAPI_COMPAT_SPEC(configure_c_stdio, BOOL, _Py_NULL),
2037+
#if 0x030D0000 <= PY_VERSION_HEX
2038+
PYTHONCAPI_COMPAT_SPEC(cpu_count, INT, _Py_NULL),
2039+
#endif
2040+
PYTHONCAPI_COMPAT_SPEC(dev_mode, BOOL, _Py_NULL),
2041+
PYTHONCAPI_COMPAT_SPEC(dump_refs, BOOL, _Py_NULL),
2042+
#if 0x030B0000 <= PY_VERSION_HEX
2043+
PYTHONCAPI_COMPAT_SPEC(dump_refs_file, WSTR_OPT, _Py_NULL),
2044+
#endif
2045+
#ifdef Py_GIL_DISABLED
2046+
PYTHONCAPI_COMPAT_SPEC(enable_gil, INT, _Py_NULL),
2047+
#endif
2048+
PYTHONCAPI_COMPAT_SPEC(faulthandler, BOOL, _Py_NULL),
2049+
PYTHONCAPI_COMPAT_SPEC(filesystem_encoding, WSTR, _Py_NULL),
2050+
PYTHONCAPI_COMPAT_SPEC(filesystem_errors, WSTR, _Py_NULL),
2051+
PYTHONCAPI_COMPAT_SPEC(hash_seed, ULONG, _Py_NULL),
2052+
PYTHONCAPI_COMPAT_SPEC(home, WSTR_OPT, _Py_NULL),
2053+
PYTHONCAPI_COMPAT_SPEC(import_time, BOOL, _Py_NULL),
2054+
PYTHONCAPI_COMPAT_SPEC(install_signal_handlers, BOOL, _Py_NULL),
2055+
PYTHONCAPI_COMPAT_SPEC(isolated, BOOL, _Py_NULL),
2056+
#ifdef MS_WINDOWS
2057+
PYTHONCAPI_COMPAT_SPEC(legacy_windows_stdio, BOOL, _Py_NULL),
2058+
#endif
2059+
PYTHONCAPI_COMPAT_SPEC(malloc_stats, BOOL, _Py_NULL),
2060+
#if 0x030A0000 <= PY_VERSION_HEX
2061+
PYTHONCAPI_COMPAT_SPEC(orig_argv, WSTR_LIST, "orig_argv"),
2062+
#endif
2063+
PYTHONCAPI_COMPAT_SPEC(parse_argv, BOOL, _Py_NULL),
2064+
PYTHONCAPI_COMPAT_SPEC(pathconfig_warnings, BOOL, _Py_NULL),
2065+
#if 0x030C0000 <= PY_VERSION_HEX
2066+
PYTHONCAPI_COMPAT_SPEC(perf_profiling, UINT, _Py_NULL),
2067+
#endif
2068+
PYTHONCAPI_COMPAT_SPEC(program_name, WSTR, _Py_NULL),
2069+
PYTHONCAPI_COMPAT_SPEC(run_command, WSTR_OPT, _Py_NULL),
2070+
PYTHONCAPI_COMPAT_SPEC(run_filename, WSTR_OPT, _Py_NULL),
2071+
PYTHONCAPI_COMPAT_SPEC(run_module, WSTR_OPT, _Py_NULL),
2072+
#if 0x030B0000 <= PY_VERSION_HEX
2073+
PYTHONCAPI_COMPAT_SPEC(safe_path, BOOL, _Py_NULL),
2074+
#endif
2075+
PYTHONCAPI_COMPAT_SPEC(show_ref_count, BOOL, _Py_NULL),
2076+
PYTHONCAPI_COMPAT_SPEC(site_import, BOOL, _Py_NULL),
2077+
PYTHONCAPI_COMPAT_SPEC(skip_source_first_line, BOOL, _Py_NULL),
2078+
PYTHONCAPI_COMPAT_SPEC(stdio_encoding, WSTR, _Py_NULL),
2079+
PYTHONCAPI_COMPAT_SPEC(stdio_errors, WSTR, _Py_NULL),
2080+
PYTHONCAPI_COMPAT_SPEC(tracemalloc, UINT, _Py_NULL),
2081+
#if 0x030B0000 <= PY_VERSION_HEX
2082+
PYTHONCAPI_COMPAT_SPEC(use_frozen_modules, BOOL, _Py_NULL),
2083+
#endif
2084+
PYTHONCAPI_COMPAT_SPEC(use_hash_seed, BOOL, _Py_NULL),
2085+
#if 0x030D0000 <= PY_VERSION_HEX && defined(__APPLE__)
2086+
PYTHONCAPI_COMPAT_SPEC(use_system_logger, BOOL, _Py_NULL),
2087+
#endif
2088+
PYTHONCAPI_COMPAT_SPEC(user_site_directory, BOOL, _Py_NULL),
2089+
#if 0x030A0000 <= PY_VERSION_HEX
2090+
PYTHONCAPI_COMPAT_SPEC(warn_default_encoding, BOOL, _Py_NULL),
2091+
#endif
2092+
};
2093+
2094+
#undef PYTHONCAPI_COMPAT_SPEC
2095+
2096+
const PyConfigSpec *spec;
2097+
int found = 0;
2098+
for (size_t i=0; i < Py_ARRAY_LENGTH(config_spec); i++) {
2099+
spec = &config_spec[i];
2100+
if (strcmp(spec->name, name) == 0) {
2101+
found = 1;
2102+
break;
2103+
}
2104+
}
2105+
if (found) {
2106+
if (spec->sys_attr != NULL) {
2107+
PyObject *value = PySys_GetObject(spec->sys_attr);
2108+
if (value == NULL) {
2109+
PyErr_Format(PyExc_RuntimeError, "lost sys.%s", spec->sys_attr);
2110+
return NULL;
2111+
}
2112+
return Py_NewRef(value);
2113+
}
2114+
2115+
extern const PyConfig* _Py_GetConfig(void);
2116+
const PyConfig *config = _Py_GetConfig();
2117+
void *member = (char *)config + spec->offset;
2118+
switch (spec->type) {
2119+
case _PyConfig_MEMBER_INT:
2120+
case _PyConfig_MEMBER_UINT:
2121+
{
2122+
int value = *(int *)member;
2123+
return PyLong_FromLong(value);
2124+
}
2125+
case _PyConfig_MEMBER_BOOL:
2126+
{
2127+
int value = *(int *)member;
2128+
return PyBool_FromLong(value != 0);
2129+
}
2130+
case _PyConfig_MEMBER_ULONG:
2131+
{
2132+
unsigned long value = *(unsigned long *)member;
2133+
return PyLong_FromUnsignedLong(value);
2134+
}
2135+
case _PyConfig_MEMBER_WSTR:
2136+
case _PyConfig_MEMBER_WSTR_OPT:
2137+
{
2138+
wchar_t *wstr = *(wchar_t **)member;
2139+
if (wstr != NULL) {
2140+
return PyUnicode_FromWideChar(wstr, -1);
2141+
}
2142+
else {
2143+
return Py_NewRef(Py_None);
2144+
}
2145+
}
2146+
case _PyConfig_MEMBER_WSTR_LIST:
2147+
{
2148+
const PyWideStringList *list = (const PyWideStringList *)member;
2149+
PyObject *tuple = PyTuple_New(list->length);
2150+
if (tuple == NULL) {
2151+
return NULL;
2152+
}
2153+
2154+
for (Py_ssize_t i = 0; i < list->length; i++) {
2155+
PyObject *item = PyUnicode_FromWideChar(list->items[i], -1);
2156+
if (item == NULL) {
2157+
Py_DECREF(tuple);
2158+
return NULL;
2159+
}
2160+
PyTuple_SET_ITEM(tuple, i, item);
2161+
}
2162+
return tuple;
2163+
}
2164+
default:
2165+
Py_UNREACHABLE();
2166+
}
2167+
}
2168+
2169+
PyErr_Format(PyExc_ValueError, "unknown config option name: %s", name);
2170+
return NULL;
2171+
}
2172+
2173+
static inline int
2174+
PyConfig_GetInt(const char *name, int *value)
2175+
{
2176+
PyObject *obj = PyConfig_Get(name);
2177+
if (obj == NULL) {
2178+
return -1;
2179+
}
2180+
2181+
if (!PyLong_Check(obj)) {
2182+
Py_DECREF(obj);
2183+
PyErr_Format(PyExc_TypeError, "config option %s is not an int", name);
2184+
return -1;
2185+
}
2186+
2187+
int as_int = PyLong_AsInt(obj);
2188+
Py_DECREF(obj);
2189+
if (as_int == -1 && PyErr_Occurred()) {
2190+
PyErr_Format(PyExc_OverflowError,
2191+
"config option %s value does not fit into a C int", name);
2192+
return -1;
2193+
}
2194+
2195+
*value = as_int;
2196+
return 0;
2197+
}
2198+
#endif // PY_VERSION_HEX > 0x03090000 && !defined(PYPY_VERSION)
2199+
2200+
19772201
#ifdef __cplusplus
19782202
}
19792203
#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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy