Skip to content

Commit 43322b6

Browse files
committed
Refactor use of lookup_maybe_method to propagate lookup errors
* lookup_maybe_method changed to accept ignorable exception type * lookup_maybe_method returning NULL now indicates exception should not be ignored * lookup_maybe_method returns NotImplemented otherwise if lookup failed * logic updated in callers of lookup_maybe_method to reconcile exceptions and fallbacks
1 parent 3a189af commit 43322b6

File tree

1 file changed

+99
-48
lines changed

1 file changed

+99
-48
lines changed

Objects/typeobject.c

Lines changed: 99 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ static PyObject *
101101
slot_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
102102

103103
static PyObject *
104-
lookup_maybe_method(PyObject *self, PyObject *attr, int *unbound);
104+
lookup_maybe_method(PyObject *self, PyObject *attr, int *unbound, PyObject* exc_ignored);
105105

106106
static int
107107
slot_tp_setattro(PyObject *self, PyObject *name, PyObject *value);
@@ -1163,13 +1163,13 @@ type_mro_modified(PyTypeObject *type, PyObject *bases) {
11631163
if (custom) {
11641164
PyObject *mro_meth, *type_mro_meth;
11651165
mro_meth = lookup_maybe_method(
1166-
(PyObject *)type, &_Py_ID(mro), &unbound);
1167-
if (mro_meth == NULL) {
1166+
(PyObject *)type, &_Py_ID(mro), &unbound, PyExc_AttributeError);
1167+
if (mro_meth == NULL || mro_meth == Py_NotImplemented) {
11681168
goto clear;
11691169
}
11701170
type_mro_meth = lookup_maybe_method(
1171-
(PyObject *)&PyType_Type, &_Py_ID(mro), &unbound);
1172-
if (type_mro_meth == NULL) {
1171+
(PyObject *)&PyType_Type, &_Py_ID(mro), &unbound, PyExc_AttributeError);
1172+
if (type_mro_meth == NULL || type_mro_meth == Py_NotImplemented) {
11731173
Py_DECREF(mro_meth);
11741174
goto clear;
11751175
}
@@ -1196,6 +1196,7 @@ type_mro_modified(PyTypeObject *type, PyObject *bases) {
11961196
return;
11971197

11981198
clear:
1199+
// TODO: clear errors from lookup, or is that handled somewhere else?
11991200
assert(!(type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN));
12001201
set_version_unlocked(type, 0); /* 0 is not a valid version tag */
12011202
type->tp_versions_used = _Py_ATTR_CACHE_UNUSED;
@@ -2800,12 +2801,17 @@ _PyObject_LookupSpecialMethod(PyObject *self, PyObject *attr, PyObject **self_or
28002801
return res;
28012802
}
28022803

2804+
/* lookup_maybe_method returns NotImplemented if lookup fails
2805+
Exception 'exc_ignored' (if not NULL) is suppressed and converted to NotImplemented,
2806+
All other errors during lookup return NULL with the error set
2807+
TODO: some callers expected None as a possible value?
2808+
*/
28032809
static PyObject *
2804-
lookup_maybe_method(PyObject *self, PyObject *attr, int *unbound)
2810+
lookup_maybe_method(PyObject *self, PyObject *attr, int *unbound, PyObject* exc_ignored)
28052811
{
28062812
PyObject *res = _PyType_LookupRef(Py_TYPE(self), attr);
28072813
if (res == NULL) {
2808-
return NULL;
2814+
Py_RETURN_NOTIMPLEMENTED;
28092815
}
28102816

28112817
if (_PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR)) {
@@ -2819,14 +2825,28 @@ lookup_maybe_method(PyObject *self, PyObject *attr, int *unbound)
28192825
Py_SETREF(res, f(res, self, (PyObject *)(Py_TYPE(self))));
28202826
}
28212827
}
2828+
2829+
if(res == NULL && exc_ignored && PyErr_ExceptionMatches(exc_ignored)){
2830+
// TODO: even though the docs say check before calling PyErr_ExceptionMatches,
2831+
// it seems like it could handle both being NULL?
2832+
2833+
// the descriptor caused exception, suppress only "exc_ignored" errors
2834+
PyErr_Clear();
2835+
Py_RETURN_NOTIMPLEMENTED;
2836+
}
2837+
28222838
return res;
28232839
}
28242840

2841+
/* lookup_method is complement of lookup_maybe_method
2842+
Does *not* supress any errors, and NotImplemented is converted to AttributeError
2843+
*/
28252844
static PyObject *
28262845
lookup_method(PyObject *self, PyObject *attr, int *unbound)
28272846
{
2828-
PyObject *res = lookup_maybe_method(self, attr, unbound);
2829-
if (res == NULL && !PyErr_Occurred()) {
2847+
PyObject *res = lookup_maybe_method(self, attr, unbound, NULL);
2848+
if (res == Py_NotImplemented) {
2849+
res = NULL;
28302850
PyErr_SetObject(PyExc_AttributeError, attr);
28312851
}
28322852
return res;
@@ -2891,11 +2911,9 @@ vectorcall_maybe(PyThreadState *tstate, PyObject *name,
28912911

28922912
int unbound;
28932913
PyObject *self = args[0];
2894-
PyObject *func = lookup_maybe_method(self, name, &unbound);
2895-
if (func == NULL) {
2896-
if (!PyErr_Occurred())
2897-
Py_RETURN_NOTIMPLEMENTED;
2898-
return NULL;
2914+
PyObject *func = lookup_maybe_method(self, name, &unbound, PyExc_AttributeError);
2915+
if (func == NULL || func == Py_NotImplemented) {
2916+
return func;
28992917
}
29002918
PyObject *retval = vectorcall_unbound(tstate, unbound, func, args, nargs);
29012919
Py_DECREF(func);
@@ -9801,15 +9819,20 @@ slot_sq_contains(PyObject *self, PyObject *value)
98019819
PyObject *func, *res;
98029820
int result = -1, unbound;
98039821

9804-
func = lookup_maybe_method(self, &_Py_ID(__contains__), &unbound);
9822+
func = lookup_maybe_method(self, &_Py_ID(__contains__), &unbound, PyExc_AttributeError);
9823+
if (func == NULL){
9824+
return -1;
9825+
}
9826+
98059827
if (func == Py_None) {
98069828
Py_DECREF(func);
98079829
PyErr_Format(PyExc_TypeError,
98089830
"'%.200s' object is not a container",
98099831
Py_TYPE(self)->tp_name);
98109832
return -1;
98119833
}
9812-
if (func != NULL) {
9834+
9835+
if (func != Py_NotImplemented) {
98139836
PyObject *args[2] = {self, value};
98149837
res = vectorcall_unbound(tstate, unbound, func, args, 2);
98159838
Py_DECREF(func);
@@ -9818,7 +9841,7 @@ slot_sq_contains(PyObject *self, PyObject *value)
98189841
Py_DECREF(res);
98199842
}
98209843
}
9821-
else if (! PyErr_Occurred()) {
9844+
else {
98229845
/* Possible results: -1 and 1 */
98239846
result = (int)_PySequence_IterSearch(self, value,
98249847
PY_ITERSEARCH_CONTAINS);
@@ -9890,19 +9913,21 @@ slot_nb_bool(PyObject *self)
98909913
int result, unbound;
98919914
int using_len = 0;
98929915

9893-
func = lookup_maybe_method(self, &_Py_ID(__bool__), &unbound);
9916+
func = lookup_maybe_method(self, &_Py_ID(__bool__), &unbound, PyExc_AttributeError);
98949917
if (func == NULL) {
9895-
if (PyErr_Occurred()) {
9918+
return -1;
9919+
}
9920+
9921+
if (func == Py_NotImplemented) {
9922+
func = lookup_maybe_method(self, &_Py_ID(__len__), &unbound, PyExc_AttributeError);
9923+
if (func == NULL) {
98969924
return -1;
98979925
}
98989926

9899-
func = lookup_maybe_method(self, &_Py_ID(__len__), &unbound);
9900-
if (func == NULL) {
9901-
if (PyErr_Occurred()) {
9902-
return -1;
9903-
}
9927+
if (func == Py_NotImplemented) {
99049928
return 1;
99059929
}
9930+
99069931
using_len = 1;
99079932
}
99089933

@@ -9982,13 +10007,17 @@ slot_tp_repr(PyObject *self)
998210007
PyObject *func, *res;
998310008
int unbound;
998410009

9985-
func = lookup_maybe_method(self, &_Py_ID(__repr__), &unbound);
9986-
if (func != NULL) {
10010+
func = lookup_maybe_method(self, &_Py_ID(__repr__), &unbound, PyExc_AttributeError);
10011+
if (func == NULL) {
10012+
return NULL;
10013+
}
10014+
10015+
if (func != Py_NotImplemented) {
998710016
res = call_unbound_noarg(unbound, func, self);
998810017
Py_DECREF(func);
998910018
return res;
999010019
}
9991-
PyErr_Clear();
10020+
999210021
return PyUnicode_FromFormat("<%s object at %p>",
999310022
Py_TYPE(self)->tp_name, self);
999410023
}
@@ -10002,13 +10031,12 @@ slot_tp_hash(PyObject *self)
1000210031
Py_ssize_t h;
1000310032
int unbound;
1000410033

10005-
func = lookup_maybe_method(self, &_Py_ID(__hash__), &unbound);
10006-
10007-
if (func == Py_None) {
10008-
Py_SETREF(func, NULL);
10034+
func = lookup_maybe_method(self, &_Py_ID(__hash__), &unbound, PyExc_AttributeError);
10035+
if (func == NULL) {
10036+
return -1;
1000910037
}
1001010038

10011-
if (func == NULL) {
10039+
if (func == Py_None || func == Py_NotImplemented) {
1001210040
return PyObject_HashNotImplemented(self);
1001310041
}
1001410042

@@ -10192,10 +10220,9 @@ slot_tp_richcompare(PyObject *self, PyObject *other, int op)
1019210220
PyThreadState *tstate = _PyThreadState_GET();
1019310221

1019410222
int unbound;
10195-
PyObject *func = lookup_maybe_method(self, name_op[op], &unbound);
10196-
if (func == NULL) {
10197-
PyErr_Clear();
10198-
Py_RETURN_NOTIMPLEMENTED;
10223+
PyObject *func = lookup_maybe_method(self, name_op[op], &unbound, PyExc_AttributeError);
10224+
if (func == NULL || func == Py_NotImplemented){
10225+
return func;
1019910226
}
1020010227

1020110228
PyObject *stack[2] = {self, other};
@@ -10210,7 +10237,11 @@ slot_tp_iter(PyObject *self)
1021010237
int unbound;
1021110238
PyObject *func, *res;
1021210239

10213-
func = lookup_maybe_method(self, &_Py_ID(__iter__), &unbound);
10240+
func = lookup_maybe_method(self, &_Py_ID(__iter__), &unbound, PyExc_AttributeError);
10241+
if (func == NULL) {
10242+
return NULL;
10243+
}
10244+
1021410245
if (func == Py_None) {
1021510246
Py_DECREF(func);
1021610247
PyErr_Format(PyExc_TypeError,
@@ -10219,15 +10250,18 @@ slot_tp_iter(PyObject *self)
1021910250
return NULL;
1022010251
}
1022110252

10222-
if (func != NULL) {
10253+
if (func != Py_NotImplemented) {
1022310254
res = call_unbound_noarg(unbound, func, self);
1022410255
Py_DECREF(func);
1022510256
return res;
1022610257
}
1022710258

10228-
PyErr_Clear();
10229-
func = lookup_maybe_method(self, &_Py_ID(__getitem__), &unbound);
10259+
func = lookup_maybe_method(self, &_Py_ID(__getitem__), &unbound, PyExc_AttributeError);
1023010260
if (func == NULL) {
10261+
return NULL;
10262+
}
10263+
10264+
if (func == Py_NotImplemented) {
1023110265
PyErr_Format(PyExc_TypeError,
1023210266
"'%.200s' object is not iterable",
1023310267
Py_TYPE(self)->tp_name);
@@ -10346,8 +10380,13 @@ slot_tp_finalize(PyObject *self)
1034610380
PyObject *exc = PyErr_GetRaisedException();
1034710381

1034810382
/* Execute __del__ method, if any. */
10349-
del = lookup_maybe_method(self, &_Py_ID(__del__), &unbound);
10350-
if (del != NULL) {
10383+
del = lookup_maybe_method(self, &_Py_ID(__del__), &unbound, PyExc_AttributeError);
10384+
if (del == NULL) {
10385+
PyErr_FormatUnraisable(
10386+
"Exception ignored while getting deallocator for %s object",
10387+
Py_TYPE(self)->tp_name);
10388+
10389+
}else if (del != Py_NotImplemented) {
1035110390
res = call_unbound_noarg(unbound, del, self);
1035210391
if (res == NULL) {
1035310392
PyErr_FormatUnraisable("Exception ignored while "
@@ -10611,8 +10650,12 @@ slot_am_await(PyObject *self)
1061110650
int unbound;
1061210651
PyObject *func, *res;
1061310652

10614-
func = lookup_maybe_method(self, &_Py_ID(__await__), &unbound);
10615-
if (func != NULL) {
10653+
func = lookup_maybe_method(self, &_Py_ID(__await__), &unbound, PyExc_AttributeError);
10654+
if (func == NULL){
10655+
return NULL;
10656+
}
10657+
10658+
if (func != Py_NotImplemented) {
1061610659
res = call_unbound_noarg(unbound, func, self);
1061710660
Py_DECREF(func);
1061810661
return res;
@@ -10629,8 +10672,12 @@ slot_am_aiter(PyObject *self)
1062910672
int unbound;
1063010673
PyObject *func, *res;
1063110674

10632-
func = lookup_maybe_method(self, &_Py_ID(__aiter__), &unbound);
10633-
if (func != NULL) {
10675+
func = lookup_maybe_method(self, &_Py_ID(__aiter__), &unbound, PyExc_AttributeError);
10676+
if (func == NULL){
10677+
return NULL;
10678+
}
10679+
10680+
if (func != Py_NotImplemented) {
1063410681
res = call_unbound_noarg(unbound, func, self);
1063510682
Py_DECREF(func);
1063610683
return res;
@@ -10647,8 +10694,12 @@ slot_am_anext(PyObject *self)
1064710694
int unbound;
1064810695
PyObject *func, *res;
1064910696

10650-
func = lookup_maybe_method(self, &_Py_ID(__anext__), &unbound);
10651-
if (func != NULL) {
10697+
func = lookup_maybe_method(self, &_Py_ID(__anext__), &unbound, PyExc_AttributeError);
10698+
if (func == NULL){
10699+
return NULL;
10700+
}
10701+
10702+
if (func != Py_NotImplemented) {
1065210703
res = call_unbound_noarg(unbound, func, self);
1065310704
Py_DECREF(func);
1065410705
return res;

0 commit comments

Comments
 (0)