Skip to content

Commit 4d0f508

Browse files
gh-108512: Add and use new replacements for PySys_GetObject()
Add functions PySys_GetAttr(), PySys_GetAttrString(), PySys_GetOptionalAttr() and PySys_GetOptionalAttrString().
1 parent 2dcc570 commit 4d0f508

25 files changed

+487
-228
lines changed

Doc/c-api/init_config.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1417,7 +1417,7 @@ initialization::
14171417
14181418
/* Specify sys.path explicitly */
14191419
/* If you want to modify the default set of paths, finish
1420-
initialization first and then use PySys_GetObject("path") */
1420+
initialization first and then use PySys_GetAttr("path") */
14211421
config.module_search_paths_set = 1;
14221422
status = PyWideStringList_Append(&config.module_search_paths,
14231423
L"/path/to/stdlib");

Doc/c-api/sys.rst

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,10 +222,55 @@ These are utility functions that make functionality from the :mod:`sys` module
222222
accessible to C code. They all work with the current interpreter thread's
223223
:mod:`sys` module's dict, which is contained in the internal thread state structure.
224224
225+
.. c:function:: PyObject *PySys_GetAttr(PyObject *name)
226+
227+
Return the object *name* from the :mod:`sys` module or ``NULL`` on failure.
228+
Set :exc:`RuntimeError` and return ``NULL`` if it does not exist.
229+
230+
If the non-existing object should not be treated as a failure, you can use
231+
:c:func:`PySys_GetOptionalAttr` instead.
232+
233+
.. versionadded:: 3.13
234+
235+
.. c:function:: PyObject *PySys_GetAttrString(const char *name)
236+
237+
This is the same as :c:func:`PySys_GetAttr`, but *name* is
238+
specified as a :c:expr:`const char*` UTF-8 encoded bytes string,
239+
rather than a :c:expr:`PyObject*`.
240+
241+
If the non-existing object should not be treated as a failure, you can use
242+
:c:func:`PySys_GetOptionalAttrString` instead.
243+
244+
.. versionadded:: 3.13
245+
246+
.. c:function:: int PySys_GetOptionalAttr(PyObject *name, PyObject **result);
247+
248+
Variant of :c:func:`PySys_GetAttr` which doesn't raise
249+
exception if the object does not exist.
250+
251+
If the object exists, set *\*result* to a new :term:`strong reference`
252+
to the object and return ``1``.
253+
If the object does not exist, set *\*result* to ``NULL`` and return ``0``,
254+
without setting an exception.
255+
If other error occurred, set an exception, set *\*result* to ``NULL`` and
256+
return ``-1``.
257+
258+
.. versionadded:: 3.13
259+
260+
.. c:function:: int PySys_GetOptionalAttrString(const char *name, PyObject **result);
261+
262+
This is the same as :c:func:`PySys_GetOptionalAttr`, but *name* is
263+
specified as a :c:expr:`const char*` UTF-8 encoded bytes string,
264+
rather than a :c:expr:`PyObject*`.
265+
266+
.. versionadded:: 3.13
267+
225268
.. c:function:: PyObject *PySys_GetObject(const char *name)
226269
227-
Return the object *name* from the :mod:`sys` module or ``NULL`` if it does
228-
not exist, without setting an exception.
270+
Similar to :c:func:`PySys_GetAttrString`, but return a :term:`borrowed
271+
reference` and return ``NULL`` *without* setting exception on failure.
272+
273+
Preserves exception that was set before the call.
229274
230275
.. c:function:: int PySys_SetObject(const char *name, PyObject *v)
231276

Doc/whatsnew/3.13.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -988,6 +988,12 @@ New Features
988988

989989
(Contributed by Serhiy Storchaka in :gh:`108511`.)
990990

991+
* Add functions :c:func:`PySys_GetAttr`, :c:func:`PySys_GetAttrString`,
992+
:c:func:`PySys_GetOptionalAttr` and :c:func:`PySys_GetOptionalAttrString`.
993+
They all are variants of :c:func:`PySys_GetObject` which return a new
994+
:term:`strong reference` and can set an exception on failure.
995+
(Contributed by Serhiy Storchaka in :gh:`108512`.)
996+
991997
* If Python is built in :ref:`debug mode <debug-build>` or :option:`with
992998
assertions <--with-assertions>`, :c:func:`PyTuple_SET_ITEM` and
993999
:c:func:`PyList_SET_ITEM` now check the index argument with an assertion.

Include/internal/pycore_sysmodule.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,6 @@ 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);
13-
1411
// Export for '_pickle' shared extension
1512
PyAPI_FUNC(size_t) _PySys_GetSizeOf(PyObject *);
1613

Include/sysmodule.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ extern "C" {
88
#endif
99

1010
PyAPI_FUNC(PyObject *) PySys_GetObject(const char *);
11+
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030D0000
12+
PyAPI_FUNC(PyObject *) PySys_GetAttr(PyObject *);
13+
PyAPI_FUNC(PyObject *) PySys_GetAttrString(const char *);
14+
PyAPI_FUNC(int) PySys_GetOptionalAttr(PyObject *, PyObject **);
15+
PyAPI_FUNC(int) PySys_GetOptionalAttrString(const char *, PyObject **);
16+
#endif
1117
PyAPI_FUNC(int) PySys_SetObject(const char *, PyObject *);
1218

1319
PyAPI_FUNC(void) PySys_WriteStdout(const char *format, ...)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add functions :c:func:`PySys_GetAttr`, :c:func:`PySys_GetAttrString`,
2+
:c:func:`PySys_GetOptionalAttr` and :c:func:`PySys_GetOptionalAttrString`.

Modules/_ctypes/callbacks.c

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -82,22 +82,6 @@ PyType_Spec cthunk_spec = {
8282

8383
/**************************************************************/
8484

85-
static void
86-
PrintError(const char *msg, ...)
87-
{
88-
char buf[512];
89-
PyObject *f = PySys_GetObject("stderr");
90-
va_list marker;
91-
92-
va_start(marker, msg);
93-
PyOS_vsnprintf(buf, sizeof(buf), msg, marker);
94-
va_end(marker);
95-
if (f != NULL && f != Py_None)
96-
PyFile_WriteString(buf, f);
97-
PyErr_Print();
98-
}
99-
100-
10185
#ifdef MS_WIN32
10286
/*
10387
* We must call AddRef() on non-NULL COM pointers we receive as arguments
@@ -116,7 +100,7 @@ TryAddRef(StgDictObject *dict, CDataObject *obj)
116100
int r = PyDict_Contains((PyObject *)dict, &_Py_ID(_needs_com_addref_));
117101
if (r <= 0) {
118102
if (r < 0) {
119-
PrintError("getting _needs_com_addref_");
103+
PySys_WriteStderr("getting _needs_com_addref_");
120104
}
121105
return;
122106
}
@@ -160,7 +144,7 @@ static void _CallPythonObject(void *mem,
160144
if (dict && dict->getfunc && !_ctypes_simple_instance(cnv)) {
161145
PyObject *v = dict->getfunc(*pArgs, dict->size);
162146
if (!v) {
163-
PrintError("create argument %zd:\n", i);
147+
PySys_WriteStderr("create argument %zd:\n", i);
164148
goto Done;
165149
}
166150
args[i] = v;
@@ -173,12 +157,12 @@ static void _CallPythonObject(void *mem,
173157
/* Hm, shouldn't we use PyCData_AtAddress() or something like that instead? */
174158
CDataObject *obj = (CDataObject *)_PyObject_CallNoArgs(cnv);
175159
if (!obj) {
176-
PrintError("create argument %zd:\n", i);
160+
PySys_WriteStderr("create argument %zd:\n", i);
177161
goto Done;
178162
}
179163
if (!CDataObject_Check(obj)) {
180164
Py_DECREF(obj);
181-
PrintError("unexpected result of create argument %zd:\n", i);
165+
PySys_WriteStderr("unexpected result of create argument %zd:\n", i);
182166
goto Done;
183167
}
184168
memcpy(obj->b_ptr, *pArgs, dict->size);
@@ -189,7 +173,7 @@ static void _CallPythonObject(void *mem,
189173
} else {
190174
PyErr_SetString(PyExc_TypeError,
191175
"cannot build parameter");
192-
PrintError("Parsing argument %zd\n", i);
176+
PySys_WriteStderr("Parsing argument %zd\n", i);
193177
goto Done;
194178
}
195179
/* XXX error handling! */

Modules/_cursesmodule.c

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3373,17 +3373,19 @@ _curses_setupterm_impl(PyObject *module, const char *term, int fd)
33733373
if (fd == -1) {
33743374
PyObject* sys_stdout;
33753375

3376-
sys_stdout = PySys_GetObject("stdout");
3376+
sys_stdout = PySys_GetAttrString("stdout");
3377+
if (sys_stdout == NULL) {
3378+
return NULL;
3379+
}
33773380

3378-
if (sys_stdout == NULL || sys_stdout == Py_None) {
3379-
PyErr_SetString(
3380-
PyCursesError,
3381-
"lost sys.stdout");
3381+
if (sys_stdout == Py_None) {
3382+
PyErr_SetString(PyCursesError, "lost sys.stdout");
3383+
Py_DECREF(sys_stdout);
33823384
return NULL;
33833385
}
33843386

33853387
fd = PyObject_AsFileDescriptor(sys_stdout);
3386-
3388+
Py_DECREF(sys_stdout);
33873389
if (fd == -1) {
33883390
return NULL;
33893391
}

Modules/_lsprof.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -716,7 +716,7 @@ profiler_enable(ProfilerObject *self, PyObject *args, PyObject *kwds)
716716
return NULL;
717717
}
718718

719-
PyObject* monitoring = _PyImport_GetModuleAttrString("sys", "monitoring");
719+
PyObject* monitoring = PySys_GetAttrString("monitoring");
720720
if (!monitoring) {
721721
return NULL;
722722
}
@@ -778,7 +778,7 @@ profiler_disable(ProfilerObject *self, PyObject* noarg)
778778
{
779779
if (self->flags & POF_ENABLED) {
780780
PyObject* result = NULL;
781-
PyObject* monitoring = _PyImport_GetModuleAttrString("sys", "monitoring");
781+
PyObject* monitoring = PySys_GetAttrString("monitoring");
782782

783783
if (!monitoring) {
784784
return NULL;
@@ -880,7 +880,7 @@ profiler_init(ProfilerObject *pObj, PyObject *args, PyObject *kw)
880880
Py_XSETREF(pObj->externalTimer, Py_XNewRef(timer));
881881
pObj->tool_id = PY_MONITORING_PROFILER_ID;
882882

883-
PyObject* monitoring = _PyImport_GetModuleAttrString("sys", "monitoring");
883+
PyObject* monitoring = PySys_GetAttrString("monitoring");
884884
if (!monitoring) {
885885
return -1;
886886
}

Modules/_pickle.c

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
#include "pycore_pystate.h" // _PyThreadState_GET()
1818
#include "pycore_runtime.h" // _Py_ID()
1919
#include "pycore_setobject.h" // _PySet_NextEntry()
20-
#include "pycore_sysmodule.h" // _PySys_GetAttr()
20+
#include "pycore_sysmodule.h" // _PySys_GetSizeOf()
2121

2222
#include <stdlib.h> // strtol()
2323

@@ -1986,49 +1986,54 @@ whichmodule(PyObject *global, PyObject *dotted_path)
19861986
assert(module_name == NULL);
19871987

19881988
/* Fallback on walking sys.modules */
1989-
PyThreadState *tstate = _PyThreadState_GET();
1990-
modules = _PySys_GetAttr(tstate, &_Py_ID(modules));
1989+
modules = PySys_GetAttr(&_Py_ID(modules));
19911990
if (modules == NULL) {
1992-
PyErr_SetString(PyExc_RuntimeError, "unable to get sys.modules");
19931991
return NULL;
19941992
}
19951993
if (PyDict_CheckExact(modules)) {
19961994
i = 0;
19971995
while (PyDict_Next(modules, &i, &module_name, &module)) {
19981996
if (_checkmodule(module_name, module, global, dotted_path) == 0) {
1997+
Py_DECREF(modules);
19991998
return Py_NewRef(module_name);
20001999
}
20012000
if (PyErr_Occurred()) {
2001+
Py_DECREF(modules);
20022002
return NULL;
20032003
}
20042004
}
20052005
}
20062006
else {
20072007
PyObject *iterator = PyObject_GetIter(modules);
20082008
if (iterator == NULL) {
2009+
Py_DECREF(modules);
20092010
return NULL;
20102011
}
20112012
while ((module_name = PyIter_Next(iterator))) {
20122013
module = PyObject_GetItem(modules, module_name);
20132014
if (module == NULL) {
20142015
Py_DECREF(module_name);
20152016
Py_DECREF(iterator);
2017+
Py_DECREF(modules);
20162018
return NULL;
20172019
}
20182020
if (_checkmodule(module_name, module, global, dotted_path) == 0) {
20192021
Py_DECREF(module);
20202022
Py_DECREF(iterator);
2023+
Py_DECREF(modules);
20212024
return module_name;
20222025
}
20232026
Py_DECREF(module);
20242027
Py_DECREF(module_name);
20252028
if (PyErr_Occurred()) {
20262029
Py_DECREF(iterator);
2030+
Py_DECREF(modules);
20272031
return NULL;
20282032
}
20292033
}
20302034
Py_DECREF(iterator);
20312035
}
2036+
Py_DECREF(modules);
20322037

20332038
/* If no module is found, use __main__. */
20342039
return &_Py_ID(__main__);

0 commit comments

Comments
 (0)