Skip to content

Commit eba1d9b

Browse files
[GR-64210] Backport to 24.2: Storing NO_VALUE in attribute removes the Key Value pair
PullRequest: graalpython/3766
2 parents 7d19644 + 0aa4e89 commit eba1d9b

File tree

2 files changed

+46
-5
lines changed

2 files changed

+46
-5
lines changed

graalpython/com.oracle.graal.python.test/src/tests/test_dict.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
1+
# Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
22
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
33
#
44
# The Universal Permissive License (UPL), Version 1.0
@@ -1267,3 +1267,13 @@ def test_dict_values_eq():
12671267
d1 = {1: 1, 2: 2, 4: 4}
12681268
assert d1.values() != d1.values()
12691269

1270+
def test_removing_attr_from_economic_map():
1271+
class Test:
1272+
pass
1273+
1274+
o = Test()
1275+
o.foo = 1
1276+
o.__dict__[42] = 10
1277+
del o.foo
1278+
1279+
assert "foo" not in o.__dict__

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/WriteAttributeToObjectNode.java

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -50,6 +50,7 @@
5050
import com.oracle.graal.python.builtins.objects.cext.structs.CFields;
5151
import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess;
5252
import com.oracle.graal.python.builtins.objects.common.HashingStorage;
53+
import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes;
5354
import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageSetItem;
5455
import com.oracle.graal.python.builtins.objects.dict.PDict;
5556
import com.oracle.graal.python.builtins.objects.object.PythonObject;
@@ -188,7 +189,7 @@ private static boolean writeToDynamicStorageManagedClass(PythonManagedClass klas
188189
}
189190

190191
// write to the dict: the basic specialization for non-classes
191-
@Specialization(guards = {"dict != null", "!isManagedClass(object)"})
192+
@Specialization(guards = {"dict != null", "!isManagedClass(object)", "!isNoValue(value)"})
192193
static boolean writeToDictNoType(@SuppressWarnings("unused") PythonObject object, TruffleString key, Object value,
193194
@Bind("this") Node inliningTarget,
194195
@SuppressWarnings("unused") @Shared("getDict") @Cached GetDictIfExistsNode getDict,
@@ -199,7 +200,7 @@ static boolean writeToDictNoType(@SuppressWarnings("unused") PythonObject object
199200
}
200201

201202
// write to the dict & PythonManagedClass -> requires calling onAttributeUpdate
202-
@Specialization(guards = {"dict != null"})
203+
@Specialization(guards = {"dict != null", "!isNoValue(value)"})
203204
boolean writeToDictBuiltinType(PythonBuiltinClass klass, TruffleString key, Object value,
204205
@Bind("this") Node inliningTarget,
205206
@SuppressWarnings("unused") @Shared("getDict") @Cached GetDictIfExistsNode getDict,
@@ -216,7 +217,7 @@ boolean writeToDictBuiltinType(PythonBuiltinClass klass, TruffleString key, Obje
216217
}
217218
}
218219

219-
@Specialization(guards = {"dict != null"})
220+
@Specialization(guards = {"dict != null", "!isNoValue(value)"})
220221
static boolean writeToDictClass(PythonClass klass, TruffleString key, Object value,
221222
@Bind("this") Node inliningTarget,
222223
@SuppressWarnings("unused") @Shared("getDict") @Cached GetDictIfExistsNode getDict,
@@ -229,6 +230,36 @@ static boolean writeToDictClass(PythonClass klass, TruffleString key, Object val
229230
return writeToDictManagedClass(klass, dict, key, value, inliningTarget, callAttrUpdate, updateStorage, setHashingStorageItem, codePointLengthNode, codePointAtIndexNode);
230231
}
231232

233+
@Specialization(guards = {"dict != null", "isNoValue(value)", "!isPythonBuiltinClass(obj)"})
234+
static boolean deleteFromPythonObject(PythonObject obj, TruffleString key, Object value,
235+
@Bind("this") Node inliningTarget,
236+
@SuppressWarnings("unused") @Shared("getDict") @Cached GetDictIfExistsNode getDict,
237+
@Bind("getDict.execute(obj)") PDict dict,
238+
@Shared("callAttrUpdate") @Cached InlinedBranchProfile callAttrUpdate,
239+
@Cached HashingStorageNodes.HashingStorageDelItem hashingStorageDelItem,
240+
@Shared("cpLen") @Cached TruffleString.CodePointLengthNode codePointLengthNode,
241+
@Shared("cpAtIndex") @Cached TruffleString.CodePointAtIndexNode codePointAtIndexNode) {
242+
try {
243+
HashingStorage dictStorage = dict.getDictStorage();
244+
return hashingStorageDelItem.execute(inliningTarget, dictStorage, key, dict);
245+
} finally {
246+
if (obj instanceof PythonManagedClass klass) {
247+
if (!klass.canSkipOnAttributeUpdate(key, value, codePointLengthNode, codePointAtIndexNode)) {
248+
callAttrUpdate.enter(inliningTarget);
249+
klass.onAttributeUpdate(key, value);
250+
}
251+
}
252+
}
253+
}
254+
255+
@Specialization(guards = {"dict != null", "isNoValue(value)"})
256+
static boolean deleteFromPythonBuiltinClass(PythonBuiltinClass klass, TruffleString key, Object value,
257+
@Bind("this") Node inliningTarget,
258+
@SuppressWarnings("unused") @Shared("getDict") @Cached GetDictIfExistsNode getDict,
259+
@Bind("getDict.execute(klass)") PDict dict) {
260+
throw PRaiseNode.raiseUncached(inliningTarget, TypeError, ErrorMessages.CANT_SET_ATTRIBUTE_R_OF_IMMUTABLE_TYPE_N, key, klass);
261+
}
262+
232263
private static boolean writeToDictManagedClass(PythonManagedClass klass, PDict dict, TruffleString key, Object value, Node inliningTarget,
233264
InlinedBranchProfile callAttrUpdate, InlinedBranchProfile updateStorage, HashingStorageSetItem setHashingStorageItem, TruffleString.CodePointLengthNode codePointLengthNode,
234265
TruffleString.CodePointAtIndexNode codePointAtIndexNode) {

0 commit comments

Comments
 (0)