From 818a6c9bc8b0c0c24e21862df5dd2fdce97c14d8 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sun, 19 Jan 2025 22:07:39 +0100 Subject: [PATCH 1/3] Add PyConfig_Get() --- docs/api.rst | 10 +- docs/changelog.rst | 1 + pythoncapi_compat.h | 230 ++++++++++++++++++++++++++++ tests/test_pythoncapi_compat_cext.c | 63 ++++++++ 4 files changed, 302 insertions(+), 2 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index a85a61d..f2dd262 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -177,11 +177,17 @@ Python 3.14 See `Py_fclose() documentation `__. +.. c:function:: PyObject* PyConfig_Get(const char *name) + + See `PyConfig_Get() documentation `__. + +.. c:function:: int PyConfig_GetInt(const char *name, int *value) + + See `PyConfig_GetInt() documentation `__. + Not supported: -* ``PyConfig_Get()`` -* ``PyConfig_GetInt()`` * ``PyConfig_Names()`` * ``PyConfig_Set()`` * ``PyInitConfig_AddModule()`` diff --git a/docs/changelog.rst b/docs/changelog.rst index 04a63b2..74e046b 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,7 @@ Changelog ========= +* 2025-01-19: Add ``PyConfig_Get()`` functions. * 2025-01-06: Add ``Py_fopen()`` and ``Py_fclose()`` functions. * 2024-12-16: Add ``structmember.h`` constants: diff --git a/pythoncapi_compat.h b/pythoncapi_compat.h index 0ea8e80..8d07ffc 100644 --- a/pythoncapi_compat.h +++ b/pythoncapi_compat.h @@ -19,6 +19,7 @@ extern "C" { #endif #include +#include // offsetof() // Python 3.11.0b4 added PyFrame_Back() to Python.h #if PY_VERSION_HEX < 0x030b00B4 && !defined(PYPY_VERSION) @@ -1974,6 +1975,235 @@ int Py_fclose(FILE *file) #endif +#if 0x03090000 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x030E0000 && !defined(PYPY_VERSION) +static inline PyObject* +PyConfig_Get(const char *name) +{ + typedef enum { + _PyConfig_MEMBER_INT, + _PyConfig_MEMBER_UINT, + _PyConfig_MEMBER_ULONG, + _PyConfig_MEMBER_BOOL, + _PyConfig_MEMBER_WSTR, + _PyConfig_MEMBER_WSTR_OPT, + _PyConfig_MEMBER_WSTR_LIST, + } PyConfigMemberType; + + typedef struct { + const char *name; + size_t offset; + PyConfigMemberType type; + const char *sys_attr; + } PyConfigSpec; + +#define PYTHONCAPI_COMPAT_SPEC(MEMBER, TYPE, sys_attr) \ + {#MEMBER, offsetof(PyConfig, MEMBER), \ + _PyConfig_MEMBER_##TYPE, sys_attr} + + static const PyConfigSpec config_spec[] = { + PYTHONCAPI_COMPAT_SPEC(argv, WSTR_LIST, "argv"), + PYTHONCAPI_COMPAT_SPEC(base_exec_prefix, WSTR_OPT, "base_exec_prefix"), + PYTHONCAPI_COMPAT_SPEC(base_executable, WSTR_OPT, "_base_executable"), + PYTHONCAPI_COMPAT_SPEC(base_prefix, WSTR_OPT, "base_prefix"), + PYTHONCAPI_COMPAT_SPEC(bytes_warning, UINT, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(exec_prefix, WSTR_OPT, "exec_prefix"), + PYTHONCAPI_COMPAT_SPEC(executable, WSTR_OPT, "executable"), + PYTHONCAPI_COMPAT_SPEC(inspect, BOOL, _Py_NULL), +#if 0x030C0000 <= PY_VERSION_HEX + PYTHONCAPI_COMPAT_SPEC(int_max_str_digits, UINT, _Py_NULL), +#endif + PYTHONCAPI_COMPAT_SPEC(interactive, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(module_search_paths, WSTR_LIST, "path"), + PYTHONCAPI_COMPAT_SPEC(optimization_level, UINT, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(parser_debug, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(platlibdir, WSTR, "platlibdir"), + PYTHONCAPI_COMPAT_SPEC(prefix, WSTR_OPT, "prefix"), + PYTHONCAPI_COMPAT_SPEC(pycache_prefix, WSTR_OPT, "pycache_prefix"), + PYTHONCAPI_COMPAT_SPEC(quiet, BOOL, _Py_NULL), +#if 0x030B0000 <= PY_VERSION_HEX + PYTHONCAPI_COMPAT_SPEC(stdlib_dir, WSTR_OPT, "_stdlib_dir"), +#endif + PYTHONCAPI_COMPAT_SPEC(use_environment, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(verbose, UINT, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(warnoptions, WSTR_LIST, "warnoptions"), + PYTHONCAPI_COMPAT_SPEC(write_bytecode, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(xoptions, WSTR_LIST, "_xoptions"), +#ifdef Py_STATS + PYTHONCAPI_COMPAT_SPEC(_pystats, BOOL, _Py_NULL), +#endif + PYTHONCAPI_COMPAT_SPEC(buffered_stdio, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(check_hash_pycs_mode, WSTR, _Py_NULL), +#if 0x030B0000 <= PY_VERSION_HEX + PYTHONCAPI_COMPAT_SPEC(code_debug_ranges, BOOL, _Py_NULL), +#endif + PYTHONCAPI_COMPAT_SPEC(configure_c_stdio, BOOL, _Py_NULL), +#if 0x030D0000 <= PY_VERSION_HEX + PYTHONCAPI_COMPAT_SPEC(cpu_count, INT, _Py_NULL), +#endif + PYTHONCAPI_COMPAT_SPEC(dev_mode, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(dump_refs, BOOL, _Py_NULL), +#if 0x030B0000 <= PY_VERSION_HEX + PYTHONCAPI_COMPAT_SPEC(dump_refs_file, WSTR_OPT, _Py_NULL), +#endif +#ifdef Py_GIL_DISABLED + PYTHONCAPI_COMPAT_SPEC(enable_gil, INT, _Py_NULL), +#endif + PYTHONCAPI_COMPAT_SPEC(faulthandler, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(filesystem_encoding, WSTR, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(filesystem_errors, WSTR, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(hash_seed, ULONG, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(home, WSTR_OPT, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(import_time, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(install_signal_handlers, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(isolated, BOOL, _Py_NULL), +#ifdef MS_WINDOWS + PYTHONCAPI_COMPAT_SPEC(legacy_windows_stdio, BOOL, _Py_NULL), +#endif + PYTHONCAPI_COMPAT_SPEC(malloc_stats, BOOL, _Py_NULL), +#if 0x030A0000 <= PY_VERSION_HEX + PYTHONCAPI_COMPAT_SPEC(orig_argv, WSTR_LIST, "orig_argv"), +#endif + PYTHONCAPI_COMPAT_SPEC(parse_argv, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(pathconfig_warnings, BOOL, _Py_NULL), +#if 0x030C0000 <= PY_VERSION_HEX + PYTHONCAPI_COMPAT_SPEC(perf_profiling, UINT, _Py_NULL), +#endif + PYTHONCAPI_COMPAT_SPEC(program_name, WSTR, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(run_command, WSTR_OPT, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(run_filename, WSTR_OPT, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(run_module, WSTR_OPT, _Py_NULL), +#ifdef Py_DEBUG + PYTHONCAPI_COMPAT_SPEC(run_presite, WSTR_OPT, _Py_NULL), +#endif +#if 0x030B0000 <= PY_VERSION_HEX + PYTHONCAPI_COMPAT_SPEC(safe_path, BOOL, _Py_NULL), +#endif + PYTHONCAPI_COMPAT_SPEC(show_ref_count, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(site_import, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(skip_source_first_line, BOOL, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(stdio_encoding, WSTR, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(stdio_errors, WSTR, _Py_NULL), + PYTHONCAPI_COMPAT_SPEC(tracemalloc, UINT, _Py_NULL), +#if 0x030B0000 <= PY_VERSION_HEX + PYTHONCAPI_COMPAT_SPEC(use_frozen_modules, BOOL, _Py_NULL), +#endif + PYTHONCAPI_COMPAT_SPEC(use_hash_seed, BOOL, _Py_NULL), +#ifdef __APPLE__ + PYTHONCAPI_COMPAT_SPEC(use_system_logger, BOOL, _Py_NULL), +#endif + PYTHONCAPI_COMPAT_SPEC(user_site_directory, BOOL, _Py_NULL), +#if 0x030A0000 <= PY_VERSION_HEX + PYTHONCAPI_COMPAT_SPEC(warn_default_encoding, BOOL, _Py_NULL), +#endif + }; + +#undef PYTHONCAPI_COMPAT_SPEC + + const PyConfigSpec *spec; + int found = 0; + for (size_t i=0; i < Py_ARRAY_LENGTH(config_spec); i++) { + spec = &config_spec[i]; + if (strcmp(spec->name, name) == 0) { + found = 1; + break; + } + } + if (found) { + if (spec->sys_attr != NULL) { + PyObject *value = PySys_GetObject(spec->sys_attr); + if (value == NULL) { + PyErr_Format(PyExc_RuntimeError, "lost sys.%s", spec->sys_attr); + return NULL; + } + return Py_NewRef(value); + } + + extern const PyConfig* _Py_GetConfig(void); + const PyConfig *config = _Py_GetConfig(); + void *member = (char *)config + spec->offset; + switch (spec->type) { + case _PyConfig_MEMBER_INT: + case _PyConfig_MEMBER_UINT: + { + int value = *(int *)member; + return PyLong_FromLong(value); + } + case _PyConfig_MEMBER_BOOL: + { + int value = *(int *)member; + return PyBool_FromLong(value != 0); + } + 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; + PyObject *tuple = PyTuple_New(list->length); + if (tuple == NULL) { + return NULL; + } + + for (Py_ssize_t i = 0; i < list->length; i++) { + PyObject *item = PyUnicode_FromWideChar(list->items[i], -1); + if (item == NULL) { + Py_DECREF(tuple); + return NULL; + } + PyTuple_SET_ITEM(tuple, i, item); + } + return tuple; + } + default: + Py_UNREACHABLE(); + } + } + + PyErr_Format(PyExc_ValueError, "unknown config option name: %s", name); + return NULL; +} + +static inline int +PyConfig_GetInt(const char *name, int *value) +{ + PyObject *obj = PyConfig_Get(name); + if (obj == NULL) { + return -1; + } + + if (!PyLong_Check(obj)) { + Py_DECREF(obj); + PyErr_Format(PyExc_TypeError, "config option %s is not an int", name); + return -1; + } + + int as_int = PyLong_AsInt(obj); + Py_DECREF(obj); + if (as_int == -1 && PyErr_Occurred()) { + PyErr_Format(PyExc_OverflowError, + "config option %s value does not fit into a C int", name); + return -1; + } + + *value = as_int; + return 0; +} +#endif // PY_VERSION_HEX > 0x03090000 && !defined(PYPY_VERSION) + + #ifdef __cplusplus } #endif diff --git a/tests/test_pythoncapi_compat_cext.c b/tests/test_pythoncapi_compat_cext.c index f1557bc..bae0ee3 100644 --- a/tests/test_pythoncapi_compat_cext.c +++ b/tests/test_pythoncapi_compat_cext.c @@ -2112,6 +2112,66 @@ test_file(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) } +#if 0x03090000 <= PY_VERSION_HEX && !defined(PYPY_VERSION) +static PyObject * +test_config(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) +{ + // Test PyConfig_Get() + PyObject *sys = PyImport_ImportModule("sys"); + if (sys == _Py_NULL) { + return _Py_NULL; + } + + PyObject *obj = PyConfig_Get("argv"); + PyObject *sys_attr = PyObject_GetAttrString(sys, "argv"); + assert(obj == sys_attr); + Py_DECREF(obj); + Py_DECREF(sys_attr); + + obj = PyConfig_Get("module_search_paths"); + sys_attr = PyObject_GetAttrString(sys, "path"); + assert(obj == sys_attr); + Py_DECREF(obj); + Py_DECREF(sys_attr); + + obj = PyConfig_Get("xoptions"); + sys_attr = PyObject_GetAttrString(sys, "_xoptions"); + assert(obj == sys_attr); + Py_DECREF(obj); + Py_DECREF(sys_attr); + + obj = PyConfig_Get("use_environment"); + assert(PyBool_Check(obj)); + Py_DECREF(obj); + + obj = PyConfig_Get("verbose"); + assert(PyLong_Check(obj)); + Py_DECREF(obj); + + assert(PyConfig_Get("nonexistent") == NULL); + assert(PyErr_ExceptionMatches(PyExc_ValueError)); + PyErr_Clear(); + + // Test PyConfig_GetInt() + int value = -3; + + assert(PyConfig_GetInt("verbose", &value) == 0); + assert(value >= 0); + + assert(PyConfig_GetInt("argv", &value) == -1); + assert(PyErr_ExceptionMatches(PyExc_TypeError)); + PyErr_Clear(); + + assert(PyConfig_GetInt("nonexistent", &value) == -1); + assert(PyErr_ExceptionMatches(PyExc_ValueError)); + PyErr_Clear(); + + Py_DECREF(sys); + Py_RETURN_NONE; +} +#endif + + static struct PyMethodDef methods[] = { {"test_object", test_object, METH_NOARGS, _Py_NULL}, {"test_py_is", test_py_is, METH_NOARGS, _Py_NULL}, @@ -2160,6 +2220,9 @@ static struct PyMethodDef methods[] = { {"test_long_stdint", test_long_stdint, METH_NOARGS, _Py_NULL}, {"test_structmember", test_structmember, METH_NOARGS, _Py_NULL}, {"test_file", test_file, METH_NOARGS, _Py_NULL}, +#if 0x03090000 <= PY_VERSION_HEX && !defined(PYPY_VERSION) + {"test_config", test_config, METH_NOARGS, _Py_NULL}, +#endif {_Py_NULL, _Py_NULL, 0, _Py_NULL} }; From 3d9b459af74d538a80ed8200bda6b780293abd82 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sun, 19 Jan 2025 22:41:46 +0100 Subject: [PATCH 2/3] Fix use_system_logger --- pythoncapi_compat.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythoncapi_compat.h b/pythoncapi_compat.h index 8d07ffc..234c408 100644 --- a/pythoncapi_compat.h +++ b/pythoncapi_compat.h @@ -2088,7 +2088,7 @@ PyConfig_Get(const char *name) PYTHONCAPI_COMPAT_SPEC(use_frozen_modules, BOOL, _Py_NULL), #endif PYTHONCAPI_COMPAT_SPEC(use_hash_seed, BOOL, _Py_NULL), -#ifdef __APPLE__ +#if 0x030D0000 <= PY_VERSION_HEX && defined(__APPLE__) PYTHONCAPI_COMPAT_SPEC(use_system_logger, BOOL, _Py_NULL), #endif PYTHONCAPI_COMPAT_SPEC(user_site_directory, BOOL, _Py_NULL), From acc066e390aaaf3a2165d1f96043017696b66d39 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sun, 19 Jan 2025 22:43:49 +0100 Subject: [PATCH 3/3] Remove debug options --- pythoncapi_compat.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pythoncapi_compat.h b/pythoncapi_compat.h index 234c408..0fc64b6 100644 --- a/pythoncapi_compat.h +++ b/pythoncapi_compat.h @@ -2028,9 +2028,6 @@ PyConfig_Get(const char *name) PYTHONCAPI_COMPAT_SPEC(warnoptions, WSTR_LIST, "warnoptions"), PYTHONCAPI_COMPAT_SPEC(write_bytecode, BOOL, _Py_NULL), PYTHONCAPI_COMPAT_SPEC(xoptions, WSTR_LIST, "_xoptions"), -#ifdef Py_STATS - PYTHONCAPI_COMPAT_SPEC(_pystats, BOOL, _Py_NULL), -#endif PYTHONCAPI_COMPAT_SPEC(buffered_stdio, BOOL, _Py_NULL), PYTHONCAPI_COMPAT_SPEC(check_hash_pycs_mode, WSTR, _Py_NULL), #if 0x030B0000 <= PY_VERSION_HEX @@ -2072,9 +2069,6 @@ PyConfig_Get(const char *name) PYTHONCAPI_COMPAT_SPEC(run_command, WSTR_OPT, _Py_NULL), PYTHONCAPI_COMPAT_SPEC(run_filename, WSTR_OPT, _Py_NULL), PYTHONCAPI_COMPAT_SPEC(run_module, WSTR_OPT, _Py_NULL), -#ifdef Py_DEBUG - PYTHONCAPI_COMPAT_SPEC(run_presite, WSTR_OPT, _Py_NULL), -#endif #if 0x030B0000 <= PY_VERSION_HEX PYTHONCAPI_COMPAT_SPEC(safe_path, BOOL, _Py_NULL), #endif 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