Skip to content

Commit d62380a

Browse files
committed
gh-129367: Add PySys_GetAttr() function
1 parent 7ec1742 commit d62380a

File tree

14 files changed

+109
-16
lines changed

14 files changed

+109
-16
lines changed

Doc/c-api/sys.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,13 @@ 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)
262+
263+
Return the object *name* from the :mod:`sys` module.
264+
265+
Return a new object on success. Set an exception and return ``NULL`` on
266+
error.
267+
261268
.. c:function:: PyObject *PySys_GetObject(const char *name)
262269
263270
Return the object *name* from the :mod:`sys` module or ``NULL`` if it does

Doc/data/refcounts.dat

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3052,3 +3052,6 @@ _Py_c_quot:Py_complex:divisor::
30523052
_Py_c_sum:Py_complex:::
30533053
_Py_c_sum:Py_complex:left::
30543054
_Py_c_sum:Py_complex:right::
3055+
3056+
PySys_GetAttr:PyObject*::+1:
3057+
PySys_GetAttr:const char*:name::

Doc/whatsnew/3.14.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1330,6 +1330,11 @@ 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`.
1336+
(Contributed by Victor Stinner in :gh:`129367`.)
1337+
13331338

13341339
Porting to Python 3.14
13351340
----------------------

Include/cpython/sysmodule.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#ifndef Py_CPYTHON_SYS_H
2+
# error "this header file must not be included directly"
3+
#endif
4+
5+
PyAPI_FUNC(PyObject *) PySys_GetAttr(const char *);

Include/sysmodule.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ Py_DEPRECATED(3.13) PyAPI_FUNC(void) PySys_ResetWarnOptions(void);
2121

2222
PyAPI_FUNC(PyObject *) PySys_GetXOptions(void);
2323

24+
#ifndef Py_LIMITED_API
25+
# define Py_CPYTHON_SYS_H
26+
# include "cpython/sysmodule.h"
27+
# undef Py_CPYTHON_SYS_H
28+
#endif
29+
2430
#ifdef __cplusplus
2531
}
2632
#endif

Lib/test/test_capi/test_sys.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,12 @@
44
from test import support
55
from test.support import import_helper
66

7-
try:
8-
import _testlimitedcapi
9-
except ImportError:
10-
_testlimitedcapi = None
7+
_testlimitedcapi = import_helper.import_module('_testlimitedcapi')
8+
_testcapi = import_helper.import_module('_testcapi')
119

1210
NULL = None
1311

12+
1413
class CAPITest(unittest.TestCase):
1514
# TODO: Test the following functions:
1615
#
@@ -20,7 +19,6 @@ class CAPITest(unittest.TestCase):
2019
maxDiff = None
2120

2221
@support.cpython_only
23-
@unittest.skipIf(_testlimitedcapi is None, 'need _testlimitedcapi module')
2422
def test_sys_getobject(self):
2523
# Test PySys_GetObject()
2624
getobject = _testlimitedcapi.sys_getobject
@@ -37,8 +35,22 @@ def test_sys_getobject(self):
3735
"'utf-8' codec can't decode")
3836
# CRASHES getobject(NULL)
3937

38+
def test_sys_getattr(self):
39+
# Test PySys_GetAttr()
40+
sys_getattr = _testcapi.PySys_GetAttr
41+
42+
self.assertIs(sys_getattr('stdout'), sys.stdout)
43+
with support.swap_attr(sys, '\U0001f40d', 42):
44+
self.assertEqual(sys_getattr('\U0001f40d'.encode()), 42)
45+
46+
with self.assertRaises(AttributeError):
47+
sys_getattr(b'nonexisting')
48+
with self.assertRaises(UnicodeDecodeError):
49+
self.assertIs(sys_getattr(b'\xff'), AttributeError)
50+
51+
# CRASHES sys_getattr(NULL)
52+
4053
@support.cpython_only
41-
@unittest.skipIf(_testlimitedcapi is None, 'need _testlimitedcapi module')
4254
def test_sys_setobject(self):
4355
# Test PySys_SetObject()
4456
setobject = _testlimitedcapi.sys_setobject
@@ -70,7 +82,6 @@ def test_sys_setobject(self):
7082
# CRASHES setobject(NULL, value)
7183

7284
@support.cpython_only
73-
@unittest.skipIf(_testlimitedcapi is None, 'need _testlimitedcapi module')
7485
def test_sys_getxoptions(self):
7586
# Test PySys_GetXOptions()
7687
getxoptions = _testlimitedcapi.sys_getxoptions
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
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.

Modules/Setup.stdlib.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@
162162
@MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c
163163
@MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
164164
@MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c _testinternalcapi/test_lock.c _testinternalcapi/pytime.c _testinternalcapi/set.c _testinternalcapi/test_critical_sections.c
165-
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c _testcapi/monitoring.c _testcapi/config.c
165+
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c _testcapi/monitoring.c _testcapi/config.c _testcapi/sys.c
166166
@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/codec.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/import.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/sys.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c _testlimitedcapi/version.c
167167
@MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c
168168
@MODULE__TESTCLINIC_LIMITED_TRUE@_testclinic_limited _testclinic_limited.c

Modules/_testcapi/parts.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,5 +61,6 @@ int _PyTestCapi_Init_Time(PyObject *module);
6161
int _PyTestCapi_Init_Monitoring(PyObject *module);
6262
int _PyTestCapi_Init_Object(PyObject *module);
6363
int _PyTestCapi_Init_Config(PyObject *mod);
64+
int _PyTestCapi_Init_Sys(PyObject *mod);
6465

6566
#endif // Py_TESTCAPI_PARTS_H

Modules/_testcapi/sys.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#include "parts.h"
2+
#include "util.h"
3+
4+
5+
static PyObject *
6+
pysys_getattr(PyObject *self, PyObject *arg)
7+
{
8+
const char *name;
9+
Py_ssize_t size;
10+
if (!PyArg_Parse(arg, "z#", &name, &size)) {
11+
return NULL;
12+
}
13+
14+
return PySys_GetAttr(name);
15+
}
16+
17+
18+
static PyMethodDef test_methods[] = {
19+
{"PySys_GetAttr", pysys_getattr, METH_O},
20+
{NULL},
21+
};
22+
23+
int
24+
_PyTestCapi_Init_Sys(PyObject *m)
25+
{
26+
return PyModule_AddFunctions(m, test_methods);
27+
}

0 commit comments

Comments
 (0)