Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c88b99c

Browse files
committed
Clear errors raised by PyObject_Compare() without losing any existing
exception context. This avoids improperly propogating errors raised by a user-defined __cmp__() by a subsequent lookup operation. This patch does *not* include the performance enhancement patch for dictionaries with string keys only; that will be checked in separately. This closes SourceForge patch #101277 and bug #112558.
1 parent a3895c0 commit c88b99c

1 file changed

Lines changed: 44 additions & 9 deletions

File tree

Objects/dictobject.c

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,10 @@ lookdict(dictobject *mp, PyObject *key, register long hash)
139139
register unsigned int mask = mp->ma_size-1;
140140
dictentry *ep0 = mp->ma_table;
141141
register dictentry *ep;
142+
register int restore_error = 0;
143+
register int checked_error = 0;
144+
register int cmp;
145+
PyObject *err_type, *err_value, *err_tb;
142146
/* We must come up with (i, incr) such that 0 <= i < ma_size
143147
and 0 < incr < ma_size and both are a function of hash */
144148
i = (~hash) & mask;
@@ -151,14 +155,25 @@ lookdict(dictobject *mp, PyObject *key, register long hash)
151155
if (ep->me_key == dummy)
152156
freeslot = ep;
153157
else {
154-
if (ep->me_hash == hash &&
155-
PyObject_Compare(ep->me_key, key) == 0)
156-
{
157-
return ep;
158+
if (ep->me_hash == hash) {
159+
/* error can't have been checked yet */
160+
checked_error = 1;
161+
if (PyErr_Occurred()) {
162+
restore_error = 1;
163+
PyErr_Fetch(&err_type, &err_value, &err_tb);
164+
}
165+
cmp = PyObject_Compare(ep->me_key, key);
166+
if (PyErr_Occurred())
167+
PyErr_Clear();
168+
else if (cmp == 0) {
169+
if (restore_error)
170+
PyErr_Restore(err_type, err_value,
171+
err_tb);
172+
return ep;
173+
}
158174
}
159175
freeslot = NULL;
160176
}
161-
/* XXX What if PyObject_Compare returned an exception? */
162177
/* Derive incr from hash, just to make it more arbitrary. Note that
163178
incr must not be 0, or we will get into an infinite loop.*/
164179
incr = (hash ^ ((unsigned long)hash >> 3)) & mask;
@@ -167,6 +182,8 @@ lookdict(dictobject *mp, PyObject *key, register long hash)
167182
for (;;) {
168183
ep = &ep0[(i+incr)&mask];
169184
if (ep->me_key == NULL) {
185+
if (restore_error)
186+
PyErr_Restore(err_type, err_value, err_tb);
170187
if (freeslot != NULL)
171188
return freeslot;
172189
else
@@ -176,12 +193,30 @@ lookdict(dictobject *mp, PyObject *key, register long hash)
176193
if (freeslot == NULL)
177194
freeslot = ep;
178195
}
179-
else if (ep->me_key == key ||
180-
(ep->me_hash == hash &&
181-
PyObject_Compare(ep->me_key, key) == 0)) {
196+
else if (ep->me_key == key) {
197+
if (restore_error)
198+
PyErr_Restore(err_type, err_value, err_tb);
182199
return ep;
200+
}
201+
else if (ep->me_hash == hash) {
202+
if (!checked_error) {
203+
checked_error = 1;
204+
if (PyErr_Occurred()) {
205+
restore_error = 1;
206+
PyErr_Fetch(&err_type, &err_value,
207+
&err_tb);
208+
}
209+
}
210+
cmp = PyObject_Compare(ep->me_key, key);
211+
if (PyErr_Occurred())
212+
PyErr_Clear();
213+
else if (cmp == 0) {
214+
if (restore_error)
215+
PyErr_Restore(err_type, err_value,
216+
err_tb);
217+
return ep;
218+
}
183219
}
184-
/* XXX What if PyObject_Compare returned an exception? */
185220
/* Cycle through GF(2^n)-{0} */
186221
incr = incr << 1;
187222
if (incr > mask)

0 commit comments

Comments
 (0)