cpython: 90572ccda12c (original) (raw)
--- a/Lib/test/test_dict.py +++ b/Lib/test/test_dict.py @@ -299,6 +299,26 @@ class DictTest(unittest.TestCase): x.fail = True self.assertRaises(Exc, d.setdefault, x, [])
- def test_setdefault_atomic(self):
# Issue #13521: setdefault() calls __hash__ and __eq__ only once.[](#l1.8)
class Hashed(object):[](#l1.9)
def __init__(self):[](#l1.10)
self.hash_count = 0[](#l1.11)
self.eq_count = 0[](#l1.12)
def __hash__(self):[](#l1.13)
self.hash_count += 1[](#l1.14)
return 42[](#l1.15)
def __eq__(self, other):[](#l1.16)
self.eq_count += 1[](#l1.17)
return id(self) == id(other)[](#l1.18)
hashed1 = Hashed()[](#l1.19)
y = {hashed1: 5}[](#l1.20)
hashed2 = Hashed()[](#l1.21)
y.setdefault(hashed2, [])[](#l1.22)
self.assertEqual(hashed1.hash_count, 1)[](#l1.23)
self.assertEqual(hashed2.hash_count, 1)[](#l1.24)
self.assertEqual(hashed1.eq_count + hashed2.eq_count, 1)[](#l1.25)
+ def test_popitem(self): # dict.popitem() for copymode in -1, +1:
--- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,9 @@ What's New in Python 3.2.3 release candi Core and Builtins ----------------- +- Issue #13521: dict.setdefault() now does only one lookup for the given key,
- Issue #13703: oCERT-2011-003: add -R command-line option and PYTHONHASHSEED environment variable, to provide an opt-in way to protect against denial of service attacks due to hash collisions within the dict and set types. Patch
--- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -510,27 +510,16 @@ void _PyObject_GC_UNTRACK(op); } - /* -Internal routine to insert a new item into the table. -Used both by the internal resize routine and by the public insert routine. -Eats a reference to key and one to value. -Returns -1 if an error occurred, or 0 on success. +Internal routine to insert a new item into the table when you have entry object. +Used by insertdict. */ static int -insertdict(register PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value) +insertdict_by_entry(register PyDictObject *mp, PyObject *key, Py_hash_t hash,
PyDictEntry *ep, PyObject *value)[](#l3.19)
- register PyDictEntry *ep;
- typedef PyDictEntry *(*lookupfunc)(PyDictObject *, PyObject *, Py_hash_t);
- assert(mp->ma_lookup != NULL);
- ep = mp->ma_lookup(mp, key, hash);
- if (ep == NULL) {
Py_DECREF(key);[](#l3.28)
Py_DECREF(value);[](#l3.29)
return -1;[](#l3.30)
- } MAINTAIN_TRACKING(mp, key, value); if (ep->me_value != NULL) { old_value = ep->me_value;
@@ -553,6 +542,28 @@ insertdict(register PyDictObject mp, Py return 0; } + +/ +Internal routine to insert a new item into the table. +Used both by the internal resize routine and by the public insert routine. +Eats a reference to key and one to value. +Returns -1 if an error occurred, or 0 on success. +*/ +static int +insertdict(register PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value) +{
- assert(mp->ma_lookup != NULL);
- ep = mp->ma_lookup(mp, key, hash);
- if (ep == NULL) {
Py_DECREF(key);[](#l3.54)
Py_DECREF(value);[](#l3.55)
return -1;[](#l3.56)
- }
- return insertdict_by_entry(mp, key, hash, ep, value);
+} + /* Internal routine used by dictresize() to insert an item which is known to be absent from the dict. This routine also assumes that @@ -776,39 +787,26 @@ PyDict_GetItemWithError(PyObject op, Py return ep->me_value; } -/ CAUTION: PyDict_SetItem() must guarantee that it won't resize the
- */ -int -PyDict_SetItem(register PyObject *op, PyObject *key, PyObject *value) +static int +dict_set_item_by_hash_or_entry(register PyObject *op, PyObject *key,
Py_hash_t hash, PyDictEntry *ep, PyObject *value)[](#l3.78)
- register Py_hash_t hash; register Py_ssize_t n_used;
- if (!PyDict_Check(op)) {
PyErr_BadInternalCall();[](#l3.85)
return -1;[](#l3.86)
- }
- assert(key);
- assert(value); mp = (PyDictObject *)op;
- if (!PyUnicode_CheckExact(key) ||
(hash = ((PyUnicodeObject *) key)->hash) == -1)[](#l3.92)
- {
hash = PyObject_Hash(key);[](#l3.94)
if (hash == -1)[](#l3.95)
return -1;[](#l3.96)
- } assert(mp->ma_fill <= mp->ma_mask); /* at least one empty slot */ n_used = mp->ma_used; Py_INCREF(value); Py_INCREF(key);
- if (insertdict(mp, key, hash, value) != 0)
return -1;[](#l3.103)
- if (ep == NULL) {
if (insertdict(mp, key, hash, value) != 0)[](#l3.105)
return -1;[](#l3.106)
- }
- else {
if (insertdict_by_entry(mp, key, hash, ep, value) != 0)[](#l3.109)
return -1;[](#l3.110)
- }
/* If we added a key, we can safely resize. Otherwise just return
+/* CAUTION: PyDict_SetItem() must guarantee that it won't resize the
- */ +int +PyDict_SetItem(register PyObject *op, PyObject *key, PyObject *value) +{
- register Py_hash_t hash;
- if (!PyDict_Check(op)) {
PyErr_BadInternalCall();[](#l3.131)
return -1;[](#l3.132)
- }
- assert(key);
- assert(value);
- if (PyUnicode_CheckExact(key)) {
hash = ((PyUnicodeObject *) key)->hash;[](#l3.137)
if (hash == -1)[](#l3.138)
hash = PyObject_Hash(key);[](#l3.139)
- }
- else {
hash = PyObject_Hash(key);[](#l3.142)
if (hash == -1)[](#l3.143)
return -1;[](#l3.144)
- }
- return dict_set_item_by_hash_or_entry(op, key, hash, NULL, value);
+} + int PyDict_DelItem(PyObject *op, PyObject *key) { @@ -1797,9 +1825,9 @@ dict_setdefault(register PyDictObject *m return NULL; val = ep->me_value; if (val == NULL) {
val = failobj;[](#l3.156)
if (PyDict_SetItem((PyObject*)mp, key, failobj))[](#l3.157)
val = NULL;[](#l3.158)