Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 3c3f150

Browse files
committedOct 16, 2024·
pythongh-125608: Trigger dictionary watchers when inline values cahnge
Dictionary watchers on an object's attributes dictionary (`object.__dict__`) were not triggered when the managed dictionary used the object's inline values.
1 parent 760872e commit 3c3f150

File tree

3 files changed

+35
-6
lines changed

3 files changed

+35
-6
lines changed
 

‎Lib/test/test_capi/test_watchers.py

+17
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,23 @@ def test_dealloc(self):
9797
del d
9898
self.assert_events(["dealloc"])
9999

100+
def test_object_dict(self):
101+
class MyObj: pass
102+
o = MyObj()
103+
104+
with self.watcher() as wid:
105+
self.watch(wid, o.__dict__)
106+
o.foo = "bar"
107+
o.foo = "baz"
108+
del o.foo
109+
self.assert_events(["new:foo:bar", "mod:foo:baz", "del:foo"])
110+
111+
with self.watcher() as wid:
112+
self.watch(wid, o.__dict__)
113+
for _ in range(100):
114+
o.foo = "bar"
115+
self.assert_events(["new:foo:bar"] + ["mod:foo:bar"] * 99)
116+
100117
def test_unwatch(self):
101118
d = {}
102119
with self.watcher() as wid:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix a bug where dictionary watchers (e.g., :c:func:`PyDict_Watch`) on an
2+
object's attribute dictionary (:attr:`~object.__dict__`) were not triggered
3+
when the object's attributes were modified.

‎Objects/dictobject.c

+15-6
Original file line numberDiff line numberDiff line change
@@ -6835,15 +6835,24 @@ store_instance_attr_lock_held(PyObject *obj, PyDictValues *values,
68356835
}
68366836

68376837
PyObject *old_value = values->values[ix];
6838+
if (old_value == NULL && value == NULL) {
6839+
PyErr_Format(PyExc_AttributeError,
6840+
"'%.100s' object has no attribute '%U'",
6841+
Py_TYPE(obj)->tp_name, name);
6842+
return -1;
6843+
}
6844+
6845+
if (dict) {
6846+
PyInterpreterState *interp = _PyInterpreterState_GET();
6847+
PyDict_WatchEvent event = (old_value == NULL ? PyDict_EVENT_ADDED :
6848+
value == NULL ? PyDict_EVENT_DELETED :
6849+
PyDict_EVENT_MODIFIED);
6850+
_PyDict_NotifyEvent(interp, event, dict, name, value);
6851+
}
6852+
68386853
FT_ATOMIC_STORE_PTR_RELEASE(values->values[ix], Py_XNewRef(value));
68396854

68406855
if (old_value == NULL) {
6841-
if (value == NULL) {
6842-
PyErr_Format(PyExc_AttributeError,
6843-
"'%.100s' object has no attribute '%U'",
6844-
Py_TYPE(obj)->tp_name, name);
6845-
return -1;
6846-
}
68476856
_PyDictValues_AddToInsertionOrder(values, ix);
68486857
if (dict) {
68496858
assert(dict->ma_values == values);

0 commit comments

Comments
 (0)
Please sign in to comment.