Skip to content

Commit d414fec

Browse files
committed
gh-106572: Deprecate PyObject_SetAttr(v, name, NULL)
* Convert PyObject_DelAttr() and PyObject_DelAttrString() macros to functions. * Add PyObject_DelAttr() and PyObject_DelAttrString() functions to the stable ABI. * Replace PyObject_SetAttr(obj, name, NULL) with PyObject_DelAttr(obj, name). * weakref proxy_setattr() calls PyObject_DelAttr() if value is NULL.
1 parent 51ea664 commit d414fec

15 files changed

+91
-18
lines changed

Doc/c-api/object.rst

+6-5
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,9 @@ Object Protocol
8585
return ``0`` on success. This is the equivalent of the Python statement
8686
``o.attr_name = v``.
8787
88-
If *v* is ``NULL``, the attribute is deleted. This behaviour is deprecated
89-
in favour of using :c:func:`PyObject_DelAttr`, but there are currently no
90-
plans to remove it.
88+
.. deprecated:: 3.13
89+
Calling ``PyObject_SetAttrString(o, attr_name, NULL)`` is deprecated:
90+
``PyObject_DelAttr(o, attr_name)`` must be used instead.
9191
9292
9393
.. c:function:: int PyObject_SetAttrString(PyObject *o, const char *attr_name, PyObject *v)
@@ -97,8 +97,9 @@ Object Protocol
9797
return ``0`` on success. This is the equivalent of the Python statement
9898
``o.attr_name = v``.
9999
100-
If *v* is ``NULL``, the attribute is deleted, but this feature is
101-
deprecated in favour of using :c:func:`PyObject_DelAttrString`.
100+
.. deprecated:: 3.13
101+
Calling ``PyObject_SetAttrString(o, attr_name, NULL)`` is deprecated:
102+
``PyObject_DelAttrString(o, attr_name)`` must be used instead.
102103
103104
104105
.. c:function:: int PyObject_GenericSetAttr(PyObject *o, PyObject *name, PyObject *value)

Doc/data/stable_abi.dat

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Doc/whatsnew/3.13.rst

+8
Original file line numberDiff line numberDiff line change
@@ -779,6 +779,14 @@ Deprecated
779779
:c:func:`PyWeakref_GetRef` on Python 3.12 and older.
780780
(Contributed by Victor Stinner in :gh:`105927`.)
781781

782+
* Deprecate ``PyObject_SetAttr(v, name, NULL)`` and
783+
``PyObject_SetAttrString(v, name, NULL)``: use ``PyObject_DelAttr(v, name)``
784+
and ``PyObject_DelAttrString(v, name)`` instead. When
785+
:c:func:`PyObject_SetAttr` is called with a ``NULL`` value, it's unclear if
786+
the caller wants to remove the attribute on purpose, or if the value is
787+
``NULL`` because of an error. Such API is error prone and should be avoided.
788+
(Contributed by Victor Stinner in :gh:`106572`.)
789+
782790
Removed
783791
-------
784792

Include/abstract.h

+2-4
Original file line numberDiff line numberDiff line change
@@ -80,25 +80,23 @@ extern "C" {
8080
8181
This is the equivalent of the Python statement o.attr_name=v. */
8282

83-
/* Implemented as a macro:
83+
/* Implemented elsewhere:
8484
8585
int PyObject_DelAttrString(PyObject *o, const char *attr_name);
8686
8787
Delete attribute named attr_name, for object o. Returns
8888
-1 on failure.
8989
9090
This is the equivalent of the Python statement: del o.attr_name. */
91-
#define PyObject_DelAttrString(O, A) PyObject_SetAttrString((O), (A), NULL)
9291

9392

94-
/* Implemented as a macro:
93+
/* Implemented elsewhere:
9594
9695
int PyObject_DelAttr(PyObject *o, PyObject *attr_name);
9796
9897
Delete attribute named attr_name, for object o. Returns -1
9998
on failure. This is the equivalent of the Python
10099
statement: del o.attr_name. */
101-
#define PyObject_DelAttr(O, A) PyObject_SetAttr((O), (A), NULL)
102100

103101

104102
/* Implemented elsewhere:

Include/object.h

+2
Original file line numberDiff line numberDiff line change
@@ -391,9 +391,11 @@ PyAPI_FUNC(PyObject *) PyObject_RichCompare(PyObject *, PyObject *, int);
391391
PyAPI_FUNC(int) PyObject_RichCompareBool(PyObject *, PyObject *, int);
392392
PyAPI_FUNC(PyObject *) PyObject_GetAttrString(PyObject *, const char *);
393393
PyAPI_FUNC(int) PyObject_SetAttrString(PyObject *, const char *, PyObject *);
394+
PyAPI_FUNC(int) PyObject_DelAttrString(PyObject *v, const char *name);
394395
PyAPI_FUNC(int) PyObject_HasAttrString(PyObject *, const char *);
395396
PyAPI_FUNC(PyObject *) PyObject_GetAttr(PyObject *, PyObject *);
396397
PyAPI_FUNC(int) PyObject_SetAttr(PyObject *, PyObject *, PyObject *);
398+
PyAPI_FUNC(int) PyObject_DelAttr(PyObject *v, PyObject *name);
397399
PyAPI_FUNC(int) PyObject_HasAttr(PyObject *, PyObject *);
398400
PyAPI_FUNC(PyObject *) PyObject_SelfIter(PyObject *);
399401
PyAPI_FUNC(PyObject *) PyObject_GenericGetAttr(PyObject *, PyObject *);

Lib/test/test_stable_abi_ctypes.py

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Deprecate ``PyObject_SetAttr(v, name, NULL)`` and
2+
``PyObject_SetAttrString(v, name, NULL)``: use ``PyObject_DelAttr(v, name)``
3+
and ``PyObject_DelAttrString(v, name)`` instead. When
4+
:c:func:`PyObject_SetAttr` is called with a ``NULL`` value, it's unclear if the
5+
caller wants to remove the attribute on purpose, or if the value is ``NULL``
6+
because of an error. Such API is error prone and should be avoided. Patch by
7+
Victor Stinner.

Misc/stable_abi.toml

+4
Original file line numberDiff line numberDiff line change
@@ -2432,3 +2432,7 @@
24322432
added = '3.13'
24332433
[function.PyWeakref_GetRef]
24342434
added = '3.13'
2435+
[function.PyObject_DelAttr]
2436+
added = '3.13'
2437+
[function.PyObject_DelAttrString]
2438+
added = '3.13'

Objects/object.c

+44-4
Original file line numberDiff line numberDiff line change
@@ -926,8 +926,8 @@ PyObject_HasAttrString(PyObject *v, const char *name)
926926
return ok;
927927
}
928928

929-
int
930-
PyObject_SetAttrString(PyObject *v, const char *name, PyObject *w)
929+
static int
930+
object_set_attr_string(PyObject *v, const char *name, PyObject *w)
931931
{
932932
PyObject *s;
933933
int res;
@@ -942,6 +942,26 @@ PyObject_SetAttrString(PyObject *v, const char *name, PyObject *w)
942942
return res;
943943
}
944944

945+
int
946+
PyObject_SetAttrString(PyObject *v, const char *name, PyObject *w)
947+
{
948+
if (w == NULL
949+
&& PyErr_WarnEx(PyExc_DeprecationWarning,
950+
"PyObject_SetAttrString(v, name, NULL) is deprecated: "
951+
"use PyObject_DelAttrString(v, name) instead",
952+
1) < 0)
953+
{
954+
return -1;
955+
}
956+
return object_set_attr_string(v, name, w);
957+
}
958+
959+
int
960+
PyObject_DelAttrString(PyObject *v, const char *name)
961+
{
962+
return object_set_attr_string(v, name, NULL);
963+
}
964+
945965
int
946966
_PyObject_IsAbstract(PyObject *obj)
947967
{
@@ -1136,8 +1156,8 @@ PyObject_HasAttr(PyObject *v, PyObject *name)
11361156
return 1;
11371157
}
11381158

1139-
int
1140-
PyObject_SetAttr(PyObject *v, PyObject *name, PyObject *value)
1159+
static int
1160+
object_set_attr(PyObject *v, PyObject *name, PyObject *value)
11411161
{
11421162
PyTypeObject *tp = Py_TYPE(v);
11431163
int err;
@@ -1185,6 +1205,26 @@ PyObject_SetAttr(PyObject *v, PyObject *name, PyObject *value)
11851205
return -1;
11861206
}
11871207

1208+
int
1209+
PyObject_SetAttr(PyObject *v, PyObject *name, PyObject *value)
1210+
{
1211+
if (value == NULL
1212+
&& PyErr_WarnEx(PyExc_DeprecationWarning,
1213+
"PyObject_SetAttr(v, name, NULL) is deprecated: "
1214+
"use PyObject_DelAttr(v, name) instead",
1215+
1) < 0)
1216+
{
1217+
return -1;
1218+
}
1219+
return object_set_attr(v, name, value);
1220+
}
1221+
1222+
int
1223+
PyObject_DelAttr(PyObject *v, PyObject *name)
1224+
{
1225+
return object_set_attr(v, name, NULL);
1226+
}
1227+
11881228
PyObject **
11891229
_PyObject_ComputedDictPointer(PyObject *obj)
11901230
{

Objects/weakrefobject.c

+7-1
Original file line numberDiff line numberDiff line change
@@ -485,7 +485,13 @@ proxy_setattr(PyObject *proxy, PyObject *name, PyObject *value)
485485
if (!proxy_check_ref(obj)) {
486486
return -1;
487487
}
488-
int res = PyObject_SetAttr(obj, name, value);
488+
int res;
489+
if (value != NULL) {
490+
res = PyObject_SetAttr(obj, name, value);
491+
}
492+
else {
493+
res = PyObject_DelAttr(obj, name);
494+
}
489495
Py_DECREF(obj);
490496
return res;
491497
}

PC/python3dll.c

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/bltinmodule.c

+2-1
Original file line numberDiff line numberDiff line change
@@ -1567,8 +1567,9 @@ static PyObject *
15671567
builtin_delattr_impl(PyObject *module, PyObject *obj, PyObject *name)
15681568
/*[clinic end generated code: output=85134bc58dff79fa input=164865623abe7216]*/
15691569
{
1570-
if (PyObject_SetAttr(obj, name, (PyObject *)NULL) != 0)
1570+
if (PyObject_DelAttr(obj, name) < 0) {
15711571
return NULL;
1572+
}
15721573
Py_RETURN_NONE;
15731574
}
15741575

Python/bytecodes.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -1240,7 +1240,7 @@ dummy_func(
12401240

12411241
inst(DELETE_ATTR, (owner --)) {
12421242
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg);
1243-
int err = PyObject_SetAttr(owner, name, (PyObject *)NULL);
1243+
int err = PyObject_DelAttr(owner, name);
12441244
DECREF_INPUTS();
12451245
ERROR_IF(err, error);
12461246
}

Python/executor_cases.c.h

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/generated_cases.c.h

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)