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.
This commit is contained in:
Fred Drake 2000-08-31 19:04:07 +00:00
parent a3895c0d29
commit c88b99ce06

View File

@ -139,6 +139,10 @@ lookdict(dictobject *mp, PyObject *key, register long hash)
register unsigned int mask = mp->ma_size-1; register unsigned int mask = mp->ma_size-1;
dictentry *ep0 = mp->ma_table; dictentry *ep0 = mp->ma_table;
register dictentry *ep; register dictentry *ep;
register int restore_error = 0;
register int checked_error = 0;
register int cmp;
PyObject *err_type, *err_value, *err_tb;
/* We must come up with (i, incr) such that 0 <= i < ma_size /* We must come up with (i, incr) such that 0 <= i < ma_size
and 0 < incr < ma_size and both are a function of hash */ and 0 < incr < ma_size and both are a function of hash */
i = (~hash) & mask; i = (~hash) & mask;
@ -151,14 +155,25 @@ lookdict(dictobject *mp, PyObject *key, register long hash)
if (ep->me_key == dummy) if (ep->me_key == dummy)
freeslot = ep; freeslot = ep;
else { else {
if (ep->me_hash == hash && if (ep->me_hash == hash) {
PyObject_Compare(ep->me_key, key) == 0) /* error can't have been checked yet */
{ checked_error = 1;
return ep; if (PyErr_Occurred()) {
restore_error = 1;
PyErr_Fetch(&err_type, &err_value, &err_tb);
}
cmp = PyObject_Compare(ep->me_key, key);
if (PyErr_Occurred())
PyErr_Clear();
else if (cmp == 0) {
if (restore_error)
PyErr_Restore(err_type, err_value,
err_tb);
return ep;
}
} }
freeslot = NULL; freeslot = NULL;
} }
/* XXX What if PyObject_Compare returned an exception? */
/* Derive incr from hash, just to make it more arbitrary. Note that /* Derive incr from hash, just to make it more arbitrary. Note that
incr must not be 0, or we will get into an infinite loop.*/ incr must not be 0, or we will get into an infinite loop.*/
incr = (hash ^ ((unsigned long)hash >> 3)) & mask; incr = (hash ^ ((unsigned long)hash >> 3)) & mask;
@ -167,6 +182,8 @@ lookdict(dictobject *mp, PyObject *key, register long hash)
for (;;) { for (;;) {
ep = &ep0[(i+incr)&mask]; ep = &ep0[(i+incr)&mask];
if (ep->me_key == NULL) { if (ep->me_key == NULL) {
if (restore_error)
PyErr_Restore(err_type, err_value, err_tb);
if (freeslot != NULL) if (freeslot != NULL)
return freeslot; return freeslot;
else else
@ -176,12 +193,30 @@ lookdict(dictobject *mp, PyObject *key, register long hash)
if (freeslot == NULL) if (freeslot == NULL)
freeslot = ep; freeslot = ep;
} }
else if (ep->me_key == key || else if (ep->me_key == key) {
(ep->me_hash == hash && if (restore_error)
PyObject_Compare(ep->me_key, key) == 0)) { PyErr_Restore(err_type, err_value, err_tb);
return ep; return ep;
}
else if (ep->me_hash == hash) {
if (!checked_error) {
checked_error = 1;
if (PyErr_Occurred()) {
restore_error = 1;
PyErr_Fetch(&err_type, &err_value,
&err_tb);
}
}
cmp = PyObject_Compare(ep->me_key, key);
if (PyErr_Occurred())
PyErr_Clear();
else if (cmp == 0) {
if (restore_error)
PyErr_Restore(err_type, err_value,
err_tb);
return ep;
}
} }
/* XXX What if PyObject_Compare returned an exception? */
/* Cycle through GF(2^n)-{0} */ /* Cycle through GF(2^n)-{0} */
incr = incr << 1; incr = incr << 1;
if (incr > mask) if (incr > mask)