Skip to content

Commit c2683b1

Browse files
committed
Add PySys_GetAttr() function
1 parent 0a8b2c5 commit c2683b1

File tree

4 files changed

+149
-0
lines changed

4 files changed

+149
-0
lines changed

docs/api.rst

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,25 @@ Latest version of the header file:
2626
`pythoncapi_compat.h <https://raw.githubusercontent.com/python/pythoncapi-compat/main/pythoncapi_compat.h>`_.
2727

2828

29+
Python 3.15
30+
-----------
31+
32+
.. c:function:: PyObject* PySys_GetAttr(const char *name)
33+
34+
See `PySys_GetAttr() documentation <https://docs.python.org/dev/c-api/sys.html#c.PySys_GetAttr>`__.
35+
36+
.. c:function:: PyObject* PySys_GetAttrString(const char *name)
37+
38+
See `PySys_GetAttrString() documentation <https://docs.python.org/dev/c-api/sys.html#c.PySys_GetAttrString>`__.
39+
40+
.. c:function:: PyObject* PySys_GetOptionalAttr(const char *name)
41+
42+
See `PySys_GetOptionalAttr() documentation <https://docs.python.org/dev/c-api/sys.html#c.PySys_GetOptionalAttr>`__.
43+
44+
.. c:function:: PyObject* PySys_GetOptionalAttrString(const char *name)
45+
46+
See `PySys_GetOptionalAttrString() documentation <https://docs.python.org/dev/c-api/sys.html#c.PySys_GetOptionalAttrString>`__.
47+
2948
Python 3.14
3049
-----------
3150

docs/changelog.rst

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

4+
* 2025-06-03: Add functions:
5+
6+
* ``PySys_GetAttr()``
7+
* ``PySys_GetAttrString()``
8+
* ``PySys_GetOptionalAttr()``
9+
* ``PySys_GetOptionalAttrString()``
10+
411
* 2025-01-19: Add ``PyConfig_Get()`` functions.
512
* 2025-01-06: Add ``Py_fopen()`` and ``Py_fclose()`` functions.
613
* 2024-12-16: Add ``structmember.h`` constants:

pythoncapi_compat.h

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2199,6 +2199,70 @@ PyConfig_GetInt(const char *name, int *value)
21992199
#endif // PY_VERSION_HEX > 0x03090000 && !defined(PYPY_VERSION)
22002200

22012201

2202+
#if PY_VERSION_HEX < 0x030F0000
2203+
static inline PyObject*
2204+
PySys_GetAttrString(const char *name)
2205+
{
2206+
#if PY_VERSION_HEX >= 0x03000000
2207+
PyObject *value = Py_XNewRef(PySys_GetObject(name));
2208+
#else
2209+
PyObject *value = Py_XNewRef(PySys_GetObject((char*)name));
2210+
#endif
2211+
if (value != NULL) {
2212+
return value;
2213+
}
2214+
if (!PyErr_Occurred()) {
2215+
PyErr_Format(PyExc_RuntimeError, "lost sys.%s", name);
2216+
}
2217+
return NULL;
2218+
}
2219+
2220+
static inline PyObject*
2221+
PySys_GetAttr(PyObject *name)
2222+
{
2223+
#if PY_VERSION_HEX >= 0x03000000
2224+
const char *name_str = PyUnicode_AsUTF8(name);
2225+
#else
2226+
const char *name_str = PyString_AsString(name);
2227+
#endif
2228+
if (name_str == NULL) {
2229+
return NULL;
2230+
}
2231+
2232+
return PySys_GetAttrString(name_str);
2233+
}
2234+
2235+
static inline int
2236+
PySys_GetOptionalAttrString(const char *name, PyObject **value)
2237+
{
2238+
#if PY_VERSION_HEX >= 0x03000000
2239+
*value = Py_XNewRef(PySys_GetObject(name));
2240+
#else
2241+
*value = Py_XNewRef(PySys_GetObject((char*)name));
2242+
#endif
2243+
if (*value != NULL) {
2244+
return 1;
2245+
}
2246+
return 0;
2247+
}
2248+
2249+
static inline int
2250+
PySys_GetOptionalAttr(PyObject *name, PyObject **value)
2251+
{
2252+
#if PY_VERSION_HEX >= 0x03000000
2253+
const char *name_str = PyUnicode_AsUTF8(name);
2254+
#else
2255+
const char *name_str = PyString_AsString(name);
2256+
#endif
2257+
if (name_str == NULL) {
2258+
return -1;
2259+
}
2260+
2261+
return PySys_GetOptionalAttrString(name_str, value);
2262+
}
2263+
#endif // PY_VERSION_HEX < 0x030F00A1
2264+
2265+
22022266
#ifdef __cplusplus
22032267
}
22042268
#endif

tests/test_pythoncapi_compat_cext.c

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2181,6 +2181,64 @@ test_config(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
21812181
#endif
21822182

21832183

2184+
static PyObject *
2185+
test_sys(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
2186+
{
2187+
const char *stdout_str = "stdout";
2188+
PyObject *stdout_obj = create_string(stdout_str);
2189+
#if PYTHON3
2190+
PyObject *sys_stdout = PySys_GetObject(stdout_str); // borrowed ref
2191+
#else
2192+
PyObject *sys_stdout = PySys_GetObject((char*)stdout_str); // borrowed ref
2193+
#endif
2194+
const char *nonexistent_str = "nonexistent";
2195+
PyObject *nonexistent_obj = create_string(nonexistent_str);
2196+
PyObject *value;
2197+
2198+
// get sys.stdout
2199+
value = PySys_GetAttr(stdout_obj);
2200+
assert(value == sys_stdout);
2201+
Py_DECREF(value);
2202+
2203+
value = PySys_GetAttrString(stdout_str);
2204+
assert(value == sys_stdout);
2205+
Py_DECREF(value);
2206+
2207+
value = UNINITIALIZED_OBJ;
2208+
assert(PySys_GetOptionalAttr(stdout_obj, &value) == 1);
2209+
assert(value == sys_stdout);
2210+
Py_DECREF(value);
2211+
2212+
value = UNINITIALIZED_OBJ;
2213+
assert(PySys_GetOptionalAttrString(stdout_str, &value) == 1);
2214+
assert(value == sys_stdout);
2215+
Py_DECREF(value);
2216+
2217+
// non existent attribute
2218+
value = PySys_GetAttr(nonexistent_obj);
2219+
assert(value == NULL);
2220+
assert(PyErr_ExceptionMatches(PyExc_RuntimeError));
2221+
PyErr_Clear();
2222+
2223+
value = PySys_GetAttrString(nonexistent_str);
2224+
assert(value == NULL);
2225+
assert(PyErr_ExceptionMatches(PyExc_RuntimeError));
2226+
PyErr_Clear();
2227+
2228+
value = UNINITIALIZED_OBJ;
2229+
assert(PySys_GetOptionalAttr(nonexistent_obj, &value) == 0);
2230+
assert(value == NULL);
2231+
2232+
value = UNINITIALIZED_OBJ;
2233+
assert(PySys_GetOptionalAttrString(nonexistent_str, &value) == 0);
2234+
assert(value == NULL);
2235+
2236+
Py_DECREF(stdout_obj);
2237+
Py_DECREF(nonexistent_obj);
2238+
Py_RETURN_NONE;
2239+
}
2240+
2241+
21842242
static struct PyMethodDef methods[] = {
21852243
{"test_object", test_object, METH_NOARGS, _Py_NULL},
21862244
{"test_py_is", test_py_is, METH_NOARGS, _Py_NULL},
@@ -2232,6 +2290,7 @@ static struct PyMethodDef methods[] = {
22322290
#if 0x03090000 <= PY_VERSION_HEX && !defined(PYPY_VERSION)
22332291
{"test_config", test_config, METH_NOARGS, _Py_NULL},
22342292
#endif
2293+
{"test_sys", test_sys, METH_NOARGS, _Py_NULL},
22352294
{_Py_NULL, _Py_NULL, 0, _Py_NULL}
22362295
};
22372296

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