Skip to content

Commit fad6874

Browse files
gh-108512: Add and use new replacements for PySys_GetObject()
Add functions PySys_GetAttr() and PySys_GetAttrString().
1 parent 63f0406 commit fad6874

33 files changed

+613
-221
lines changed

Doc/c-api/init_config.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2098,7 +2098,7 @@ initialization::
20982098
20992099
/* Specify sys.path explicitly */
21002100
/* If you want to modify the default set of paths, finish
2101-
initialization first and then use PySys_GetObject("path") */
2101+
initialization first and then use PySys_GetAttrString("path", ...) */
21022102
config.module_search_paths_set = 1;
21032103
status = PyWideStringList_Append(&config.module_search_paths,
21042104
L"/path/to/stdlib");

Doc/c-api/sys.rst

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,11 +258,36 @@ 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:: int PySys_GetAttr(PyObject *name, PyObject **result);
262+
263+
Get the attribute *name* of the :mod:`sys` module.
264+
265+
If the object exists, set *\*result* to a new :term:`strong reference`
266+
to the object and return ``1``.
267+
If the object does not exist, set *\*result* to ``NULL`` and return ``0``,
268+
without setting an exception.
269+
If other error occurred, set an exception, set *\*result* to ``NULL`` and
270+
return ``-1``.
271+
272+
.. versionadded:: next
273+
274+
.. c:function:: int PySys_GetAttrString(const char *name, PyObject **result);
275+
276+
This is the same as :c:func:`PySys_GetAttr`, but *name* is
277+
specified as a :c:expr:`const char*` UTF-8 encoded bytes string,
278+
rather than a :c:expr:`PyObject*`.
279+
280+
.. versionadded:: next
281+
261282
.. c:function:: PyObject *PySys_GetObject(const char *name)
262283
263284
Return the object *name* from the :mod:`sys` module or ``NULL`` if it does
264285
not exist, without setting an exception.
265286
287+
Preserves exception that was set before the call.
288+
289+
It is recommended to use :c:func:`PySys_GetAttrString` instead.
290+
266291
.. c:function:: int PySys_SetObject(const char *name, PyObject *v)
267292
268293
Set *name* in the :mod:`sys` module to *v* unless *v* is ``NULL``, in which

Doc/data/stable_abi.dat

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Doc/whatsnew/3.14.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1296,6 +1296,10 @@ New features
12961296

12971297
(Contributed by Sergey B Kirpichev and Victor Stinner in :gh:`102471`.)
12981298

1299+
* Add :c:func:`PySys_GetAttr` and :c:func:`PySys_GetAttrString`
1300+
functions as replacements for :c:func:`PySys_GetObject`.
1301+
(Contributed by Serhiy Storchaka in :gh:`108512`.)
1302+
12991303
* Add :c:func:`PyType_GetBaseByToken` and :c:data:`Py_tp_token` slot for easier
13001304
superclass identification, which attempts to resolve the `type checking issue
13011305
<https://peps.python.org/pep-0630/#type-checking>`__ mentioned in :pep:`630`

Include/cpython/sysmodule.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#ifndef Py_CPYTHON_SYSMODULE_H
2+
# error "this header file must not be included directly"
3+
#endif
4+
5+
typedef int(*Py_AuditHookFunction)(const char *, PyObject *, void *);
6+
7+
PyAPI_FUNC(int) PySys_Audit(
8+
const char *event,
9+
const char *format,
10+
...);
11+
PyAPI_FUNC(int) PySys_AddAuditHook(Py_AuditHookFunction, void*);
12+
13+
typedef struct {
14+
FILE* perf_map;
15+
PyThread_type_lock map_lock;
16+
} PerfMapState;
17+
18+
PyAPI_FUNC(int) PyUnstable_PerfMapState_Init(void);
19+
PyAPI_FUNC(int) PyUnstable_WritePerfMapEntry(
20+
const void *code_addr,
21+
unsigned int code_size,
22+
const char *entry_name);
23+
PyAPI_FUNC(void) PyUnstable_PerfMapState_Fini(void);

Include/internal/pycore_sysmodule.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ extern "C" {
88
# error "this header requires Py_BUILD_CORE define"
99
#endif
1010

11-
// Export for '_pickle' shared extension
12-
PyAPI_FUNC(PyObject*) _PySys_GetAttr(PyThreadState *tstate, PyObject *name);
11+
PyAPI_FUNC(PyObject *) _PySys_GetRequiredAttr(PyObject *);
12+
PyAPI_FUNC(PyObject *) _PySys_GetRequiredAttrString(const char *);
1313

1414
// Export for '_pickle' shared extension
1515
PyAPI_FUNC(size_t) _PySys_GetSizeOf(PyObject *);

Include/sysmodule.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
extern "C" {
55
#endif
66

7+
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030e0000
8+
PyAPI_FUNC(int) PySys_GetAttr(PyObject *, PyObject **);
9+
PyAPI_FUNC(int) PySys_GetAttrString(const char *, PyObject **);
10+
#endif
711
PyAPI_FUNC(PyObject *) PySys_GetObject(const char *);
812
PyAPI_FUNC(int) PySys_SetObject(const char *, PyObject *);
913

Lib/test/test_capi/test_sys.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@
99
except ImportError:
1010
_testlimitedcapi = None
1111

12+
try:
13+
import _testinternalcapi
14+
except ImportError:
15+
_testinternalcapi = None
16+
1217
NULL = None
1318

1419
class CAPITest(unittest.TestCase):
@@ -19,6 +24,33 @@ class CAPITest(unittest.TestCase):
1924

2025
maxDiff = None
2126

27+
@unittest.skipIf(_testlimitedcapi is None, 'need _testlimitedcapi module')
28+
def test_sys_getattr(self):
29+
# Test PySys_GetAttr()
30+
sys_getattr = _testlimitedcapi.sys_getattr
31+
32+
self.assertIs(sys_getattr('stdout'), sys.stdout)
33+
with support.swap_attr(sys, '\U0001f40d', 42):
34+
self.assertEqual(sys_getattr('\U0001f40d'), 42)
35+
36+
self.assertIs(sys_getattr('nonexisting'), AttributeError)
37+
self.assertRaises(TypeError, sys_getattr, 1)
38+
self.assertRaises(TypeError, sys_getattr, [])
39+
# CRASHES sys_getattr(NULL)
40+
41+
@unittest.skipIf(_testlimitedcapi is None, 'need _testlimitedcapi module')
42+
def test_sys_getattrstring(self):
43+
# Test PySys_GetAttrString()
44+
getattrstring = _testlimitedcapi.sys_getattrstring
45+
46+
self.assertIs(getattrstring(b'stdout'), sys.stdout)
47+
with support.swap_attr(sys, '\U0001f40d', 42):
48+
self.assertEqual(getattrstring('\U0001f40d'.encode()), 42)
49+
50+
self.assertIs(getattrstring(b'nonexisting'), AttributeError)
51+
self.assertRaises(UnicodeDecodeError, getattrstring, b'\xff')
52+
# CRASHES getattrstring(NULL)
53+
2254
@support.cpython_only
2355
@unittest.skipIf(_testlimitedcapi is None, 'need _testlimitedcapi module')
2456
def test_sys_getobject(self):

Lib/test/test_stable_abi_ctypes.py

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add functions :c:func:`PySys_GetAttr` and :c:func:`PySys_GetAttrString`.

0 commit comments

Comments
 (0)