mirror of
https://github.com/python/cpython.git
synced 2025-11-25 12:44:13 +00:00
gh-132617: Fix dict.update() mutation check (gh-134815)
Some checks are pending
Tests / Windows MSI (push) Blocked by required conditions
Tests / (push) Blocked by required conditions
Tests / Change detection (push) Waiting to run
Tests / Docs (push) Blocked by required conditions
Tests / Check if Autoconf files are up to date (push) Blocked by required conditions
Tests / Check if generated files are up to date (push) Blocked by required conditions
Tests / Ubuntu SSL tests with OpenSSL (push) Blocked by required conditions
Tests / Cross build Linux (push) Blocked by required conditions
Tests / WASI (push) Blocked by required conditions
Tests / Hypothesis tests on Ubuntu (push) Blocked by required conditions
Tests / Address sanitizer (push) Blocked by required conditions
Tests / CIFuzz (push) Blocked by required conditions
Tests / All required checks pass (push) Blocked by required conditions
Lint / lint (push) Waiting to run
mypy / Run mypy on Lib/_pyrepl (push) Waiting to run
mypy / Run mypy on Lib/test/libregrtest (push) Waiting to run
mypy / Run mypy on Lib/tomllib (push) Waiting to run
mypy / Run mypy on Tools/build (push) Waiting to run
mypy / Run mypy on Tools/cases_generator (push) Waiting to run
mypy / Run mypy on Tools/clinic (push) Waiting to run
mypy / Run mypy on Tools/jit (push) Waiting to run
mypy / Run mypy on Tools/peg_generator (push) Waiting to run
Some checks are pending
Tests / Windows MSI (push) Blocked by required conditions
Tests / (push) Blocked by required conditions
Tests / Change detection (push) Waiting to run
Tests / Docs (push) Blocked by required conditions
Tests / Check if Autoconf files are up to date (push) Blocked by required conditions
Tests / Check if generated files are up to date (push) Blocked by required conditions
Tests / Ubuntu SSL tests with OpenSSL (push) Blocked by required conditions
Tests / Cross build Linux (push) Blocked by required conditions
Tests / WASI (push) Blocked by required conditions
Tests / Hypothesis tests on Ubuntu (push) Blocked by required conditions
Tests / Address sanitizer (push) Blocked by required conditions
Tests / CIFuzz (push) Blocked by required conditions
Tests / All required checks pass (push) Blocked by required conditions
Lint / lint (push) Waiting to run
mypy / Run mypy on Lib/_pyrepl (push) Waiting to run
mypy / Run mypy on Lib/test/libregrtest (push) Waiting to run
mypy / Run mypy on Lib/tomllib (push) Waiting to run
mypy / Run mypy on Tools/build (push) Waiting to run
mypy / Run mypy on Tools/cases_generator (push) Waiting to run
mypy / Run mypy on Tools/clinic (push) Waiting to run
mypy / Run mypy on Tools/jit (push) Waiting to run
mypy / Run mypy on Tools/peg_generator (push) Waiting to run
Use `ma_used` instead of `ma_keys->dk_nentries` for modification check so that we only check if the dictionary is modified, not if new keys are added to a different dictionary that shared the same keys object.
This commit is contained in:
parent
4c15505071
commit
d8994b0a77
3 changed files with 37 additions and 2 deletions
|
|
@ -290,6 +290,38 @@ class DictTest(unittest.TestCase):
|
||||||
['Cannot convert dictionary update sequence element #0 to a sequence'],
|
['Cannot convert dictionary update sequence element #0 to a sequence'],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_update_shared_keys(self):
|
||||||
|
class MyClass: pass
|
||||||
|
|
||||||
|
# Subclass str to enable us to create an object during the
|
||||||
|
# dict.update() call.
|
||||||
|
class MyStr(str):
|
||||||
|
def __hash__(self):
|
||||||
|
return super().__hash__()
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
# Create an object that shares the same PyDictKeysObject as
|
||||||
|
# obj.__dict__.
|
||||||
|
obj2 = MyClass()
|
||||||
|
obj2.a = "a"
|
||||||
|
obj2.b = "b"
|
||||||
|
obj2.c = "c"
|
||||||
|
return super().__eq__(other)
|
||||||
|
|
||||||
|
obj = MyClass()
|
||||||
|
obj.a = "a"
|
||||||
|
obj.b = "b"
|
||||||
|
|
||||||
|
x = {}
|
||||||
|
x[MyStr("a")] = MyStr("a")
|
||||||
|
|
||||||
|
# gh-132617: this previously raised "dict mutated during update" error
|
||||||
|
x.update(obj.__dict__)
|
||||||
|
|
||||||
|
self.assertEqual(x, {
|
||||||
|
MyStr("a"): "a",
|
||||||
|
"b": "b",
|
||||||
|
})
|
||||||
|
|
||||||
def test_fromkeys(self):
|
def test_fromkeys(self):
|
||||||
self.assertEqual(dict.fromkeys('abc'), {'a':None, 'b':None, 'c':None})
|
self.assertEqual(dict.fromkeys('abc'), {'a':None, 'b':None, 'c':None})
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
Fix :meth:`dict.update` modification check that could incorrectly raise a
|
||||||
|
"dict mutated during update" error when a different dictionary was modified
|
||||||
|
that happens to share the same underlying keys object.
|
||||||
|
|
@ -3858,7 +3858,7 @@ dict_dict_merge(PyInterpreterState *interp, PyDictObject *mp, PyDictObject *othe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Py_ssize_t orig_size = other->ma_keys->dk_nentries;
|
Py_ssize_t orig_size = other->ma_used;
|
||||||
Py_ssize_t pos = 0;
|
Py_ssize_t pos = 0;
|
||||||
Py_hash_t hash;
|
Py_hash_t hash;
|
||||||
PyObject *key, *value;
|
PyObject *key, *value;
|
||||||
|
|
@ -3892,7 +3892,7 @@ dict_dict_merge(PyInterpreterState *interp, PyDictObject *mp, PyDictObject *othe
|
||||||
if (err != 0)
|
if (err != 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (orig_size != other->ma_keys->dk_nentries) {
|
if (orig_size != other->ma_used) {
|
||||||
PyErr_SetString(PyExc_RuntimeError,
|
PyErr_SetString(PyExc_RuntimeError,
|
||||||
"dict mutated during update");
|
"dict mutated during update");
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue