@@ -101,7 +101,7 @@ static PyObject *
101101slot_tp_new (PyTypeObject * type , PyObject * args , PyObject * kwds );
102102
103103static 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
106106static int
107107slot_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+ */
28032809static 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+ */
28252844static PyObject *
28262845lookup_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