Skip to content

Commit 2231e0a

Browse files
committed
Add PySys_GetAttrString()
1 parent d62380a commit 2231e0a

File tree

8 files changed

+88
-27
lines changed

8 files changed

+88
-27
lines changed

Doc/c-api/sys.rst

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,13 +258,24 @@ These are utility functions that make functionality from the :mod:`sys` module
258258
accessible to C code. They all work with the current interpreter thread's
259259
:mod:`sys` module's dict, which is contained in the internal thread state structure.
260260
261-
.. c:function:: PyObject *PySys_GetAttr(const char *name)
261+
.. c:function:: PyObject *PySys_GetAttr(PyObject *name)
262262
263263
Return the object *name* from the :mod:`sys` module.
264264
265265
Return a new object on success. Set an exception and return ``NULL`` on
266266
error.
267267
268+
.. versionadded:: next
269+
270+
271+
.. c:function:: PyObject *PySys_GetAttrString(const char *name)
272+
273+
Similar to :c:func:`PySys_GetAttrString`, but *name* is an UTF-8 encoded
274+
string.
275+
276+
.. versionadded:: next
277+
278+
268279
.. c:function:: PyObject *PySys_GetObject(const char *name)
269280
270281
Return the object *name* from the :mod:`sys` module or ``NULL`` if it does

Doc/data/refcounts.dat

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3054,4 +3054,7 @@ _Py_c_sum:Py_complex:left::
30543054
_Py_c_sum:Py_complex:right::
30553055

30563056
PySys_GetAttr:PyObject*::+1:
3057-
PySys_GetAttr:const char*:name::
3057+
PySys_GetAttr:PyObject*:name:0:
3058+
3059+
PySys_GetAttrString:PyObject*::+1:
3060+
PySys_GetAttrString:const char*:name::

Doc/whatsnew/3.14.rst

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1330,9 +1330,10 @@ New features
13301330
bit-packing Python version numbers.
13311331
(Contributed by Petr Viktorin in :gh:`128629`.)
13321332

1333-
* Add :c:func:`PySys_GetAttr` function to get an attribute of the :mod:`sys`
1334-
module. Compared to :c:func:`PySys_GetObject`, it doesn't ignore errors and
1335-
it returns a :term:`strong reference`.
1333+
* Add :c:func:`PySys_GetAttr` and :c:func:`PySys_GetAttrString` functions to
1334+
get an attribute of the :mod:`sys` module. Compared to
1335+
:c:func:`PySys_GetObject`, they don't ignore errors and return a
1336+
:term:`strong reference`.
13361337
(Contributed by Victor Stinner in :gh:`129367`.)
13371338

13381339

Include/cpython/sysmodule.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
# error "this header file must not be included directly"
33
#endif
44

5-
PyAPI_FUNC(PyObject *) PySys_GetAttr(const char *);
5+
PyAPI_FUNC(PyObject *) PySys_GetAttr(PyObject *name);
6+
PyAPI_FUNC(PyObject *) PySys_GetAttrString(const char *name);

Lib/test/test_capi/test_sys.py

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,20 @@ class CAPITest(unittest.TestCase):
1818

1919
maxDiff = None
2020

21+
def check_sys_getattr_common(self, sys_getattr, use_bytes=True):
22+
self.assertIs(sys_getattr('stdout'), sys.stdout)
23+
24+
name = '\U0001f40d'
25+
with support.swap_attr(sys, name, 42):
26+
key = (name.encode() if use_bytes else name)
27+
self.assertEqual(sys_getattr(key), 42)
28+
2129
@support.cpython_only
2230
def test_sys_getobject(self):
2331
# Test PySys_GetObject()
2432
getobject = _testlimitedcapi.sys_getobject
2533

26-
self.assertIs(getobject(b'stdout'), sys.stdout)
27-
with support.swap_attr(sys, '\U0001f40d', 42):
28-
self.assertEqual(getobject('\U0001f40d'.encode()), 42)
34+
self.check_sys_getattr_common(getobject)
2935

3036
self.assertIs(getobject(b'nonexisting'), AttributeError)
3137
with support.catch_unraisable_exception() as cm:
@@ -35,21 +41,36 @@ def test_sys_getobject(self):
3541
"'utf-8' codec can't decode")
3642
# CRASHES getobject(NULL)
3743

44+
def check_sys_getattr(self, sys_getattr):
45+
self.check_sys_getattr_common(sys_getattr, use_bytes=False)
46+
47+
with self.assertRaises(AttributeError):
48+
sys_getattr(b'nonexisting')
49+
3850
def test_sys_getattr(self):
3951
# Test PySys_GetAttr()
4052
sys_getattr = _testcapi.PySys_GetAttr
4153

42-
self.assertIs(sys_getattr('stdout'), sys.stdout)
43-
with support.swap_attr(sys, '\U0001f40d', 42):
44-
self.assertEqual(sys_getattr('\U0001f40d'.encode()), 42)
54+
self.check_sys_getattr(sys_getattr)
4555

46-
with self.assertRaises(AttributeError):
47-
sys_getattr(b'nonexisting')
48-
with self.assertRaises(UnicodeDecodeError):
49-
self.assertIs(sys_getattr(b'\xff'), AttributeError)
56+
with self.assertRaises(TypeError):
57+
for invalid_name in (123, [], object()):
58+
with self.assertRaises(AttributeError):
59+
sys_getattr(invalid_name)
5060

5161
# CRASHES sys_getattr(NULL)
5262

63+
def test_sys_getattrstring(self):
64+
# Test PySys_GetAttr()
65+
sys_getattrstring = _testcapi.PySys_GetAttrString
66+
67+
self.check_sys_getattr(sys_getattrstring)
68+
69+
with self.assertRaises(UnicodeDecodeError):
70+
self.assertIs(sys_getattrstring(b'\xff'), AttributeError)
71+
72+
# CRASHES sys_getattrstring(NULL)
73+
5374
@support.cpython_only
5475
def test_sys_setobject(self):
5576
# Test PySys_SetObject()
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1-
Add :c:func:`PySys_GetAttr` function to get an attribute of the :mod:`sys`
2-
module. Compared to :c:func:`PySys_GetObject`, it doesn't ignore errors and
3-
it returns a :term:`strong reference`. Patch by Victor Stinner.
1+
Add :c:func:`PySys_GetAttr` and :c:func:`PySys_GetAttrString` functions to get
2+
an attribute of the :mod:`sys` module. Compared to :c:func:`PySys_GetObject`,
3+
they don't ignore errors and return a :term:`strong reference`. Patch by Victor
4+
Stinner.

Modules/_testcapi/sys.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,29 @@
33

44

55
static PyObject *
6-
pysys_getattr(PyObject *self, PyObject *arg)
6+
pysys_getattr(PyObject *self, PyObject *name)
7+
{
8+
NULLABLE(name);
9+
return PySys_GetAttr(name);
10+
}
11+
12+
13+
static PyObject *
14+
pysys_getattrstring(PyObject *self, PyObject *arg)
715
{
816
const char *name;
917
Py_ssize_t size;
1018
if (!PyArg_Parse(arg, "z#", &name, &size)) {
1119
return NULL;
1220
}
1321

14-
return PySys_GetAttr(name);
22+
return PySys_GetAttrString(name);
1523
}
1624

1725

1826
static PyMethodDef test_methods[] = {
1927
{"PySys_GetAttr", pysys_getattr, METH_O},
28+
{"PySys_GetAttrString", pysys_getattrstring, METH_O},
2029
{NULL},
2130
};
2231

Python/sysmodule.c

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -88,16 +88,16 @@ _PySys_GetAttr(PyThreadState *tstate, PyObject *name)
8888

8989

9090
PyObject*
91-
PySys_GetAttr(const char *name)
91+
PySys_GetAttr(PyObject *name)
9292
{
9393
PyInterpreterState *interp = _PyInterpreterState_GET();
94-
PyObject *sysdict = interp->sysdict;
95-
if (sysdict == NULL) {
94+
PyObject *sys_dict = interp->sysdict;
95+
if (sys_dict == NULL) {
9696
PyErr_SetString(PyExc_RuntimeError, "lost sys module");
9797
return NULL;
9898
}
9999
PyObject *value;
100-
if (PyDict_GetItemStringRef(sysdict, name, &value) < 0) {
100+
if (PyDict_GetItemRef(sys_dict, name, &value) < 0) {
101101
return NULL;
102102
}
103103
if (value == NULL) {
@@ -108,6 +108,20 @@ PySys_GetAttr(const char *name)
108108
}
109109

110110

111+
PyObject*
112+
PySys_GetAttrString(const char *name)
113+
{
114+
PyObject *name_obj = PyUnicode_FromString(name);
115+
if (name_obj == NULL) {
116+
return NULL;
117+
}
118+
119+
PyObject *value = PySys_GetAttr(name_obj);
120+
Py_DECREF(name_obj);
121+
return value;
122+
}
123+
124+
111125
static PyObject *
112126
_PySys_GetObject(PyInterpreterState *interp, const char *name)
113127
{
@@ -3172,7 +3186,7 @@ sys_set_flag(PyObject *flags, Py_ssize_t pos, PyObject *value)
31723186
int
31733187
_PySys_SetFlagObj(Py_ssize_t pos, PyObject *value)
31743188
{
3175-
PyObject *flags = PySys_GetAttr("flags");
3189+
PyObject *flags = PySys_GetAttrString("flags");
31763190
if (flags == NULL) {
31773191
return -1;
31783192
}
@@ -3732,7 +3746,7 @@ _PySys_UpdateConfig(PyThreadState *tstate)
37323746
#undef COPY_WSTR
37333747

37343748
// sys.flags
3735-
PyObject *flags = PySys_GetAttr("flags");
3749+
PyObject *flags = PySys_GetAttrString("flags");
37363750
if (flags == NULL) {
37373751
return -1;
37383752
}

0 commit comments

Comments
 (0)