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

Skip to content

Commit f21c6be

Browse files
committed
Add call_maybe(): a variant of call_method() that returns
NotImplemented when the lookup fails, and use this for binary operators. Also lookup_maybe() which doesn't raise an exception when the lookup fails (still returning NULL).
1 parent f2a5f3f commit f21c6be

1 file changed

Lines changed: 67 additions & 10 deletions

File tree

Objects/typeobject.c

Lines changed: 67 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -300,15 +300,23 @@ PyType_IsSubtype(PyTypeObject *a, PyTypeObject *b)
300300
}
301301
}
302302

303-
/* Internal routine to do a method lookup in the type
303+
/* Internal routines to do a method lookup in the type
304304
without looking in the instance dictionary
305305
(so we can't use PyObject_GetAttr) but still binding
306306
it to the instance. The arguments are the object,
307307
the method name as a C string, and the address of a
308-
static variable used to cache the interned Python string. */
308+
static variable used to cache the interned Python string.
309+
310+
Two variants:
311+
312+
- lookup_maybe() returns NULL without raising an exception
313+
when the _PyType_Lookup() call fails;
314+
315+
- lookup_method() always raises an exception upon errors.
316+
*/
309317

310318
static PyObject *
311-
lookup_method(PyObject *self, char *attrstr, PyObject **attrobj)
319+
lookup_maybe(PyObject *self, char *attrstr, PyObject **attrobj)
312320
{
313321
PyObject *res;
314322

@@ -318,9 +326,7 @@ lookup_method(PyObject *self, char *attrstr, PyObject **attrobj)
318326
return NULL;
319327
}
320328
res = _PyType_Lookup(self->ob_type, *attrobj);
321-
if (res == NULL)
322-
PyErr_SetObject(PyExc_AttributeError, *attrobj);
323-
else {
329+
if (res != NULL) {
324330
descrgetfunc f;
325331
if ((f = res->ob_type->tp_descr_get) == NULL)
326332
Py_INCREF(res);
@@ -330,6 +336,15 @@ lookup_method(PyObject *self, char *attrstr, PyObject **attrobj)
330336
return res;
331337
}
332338

339+
static PyObject *
340+
lookup_method(PyObject *self, char *attrstr, PyObject **attrobj)
341+
{
342+
PyObject *res = lookup_maybe(self, attrstr, attrobj);
343+
if (res == NULL && !PyErr_Occurred())
344+
PyErr_SetObject(PyExc_AttributeError, *attrobj);
345+
return res;
346+
}
347+
333348
/* A variation of PyObject_CallMethod that uses lookup_method()
334349
instead of PyObject_GetAttrString(). This uses the same convention
335350
as lookup_method to cache the interned name string object. */
@@ -342,11 +357,53 @@ call_method(PyObject *o, char *name, PyObject **nameobj, char *format, ...)
342357
PyObject *dummy_str = NULL;
343358
va_start(va, format);
344359

345-
func = lookup_method(o, name, &dummy_str);
360+
func = lookup_maybe(o, name, &dummy_str);
361+
if (func == NULL) {
362+
va_end(va);
363+
if (!PyErr_Occurred())
364+
PyErr_SetObject(PyExc_AttributeError, dummy_str);
365+
Py_XDECREF(dummy_str);
366+
return NULL;
367+
}
368+
Py_DECREF(dummy_str);
369+
370+
if (format && *format)
371+
args = Py_VaBuildValue(format, va);
372+
else
373+
args = PyTuple_New(0);
374+
375+
va_end(va);
376+
377+
if (args == NULL)
378+
return NULL;
379+
380+
assert(PyTuple_Check(args));
381+
retval = PyObject_Call(func, args, NULL);
382+
383+
Py_DECREF(args);
384+
Py_DECREF(func);
385+
386+
return retval;
387+
}
388+
389+
/* Clone of call_method() that returns NotImplemented when the lookup fails. */
390+
391+
PyObject *
392+
call_maybe(PyObject *o, char *name, PyObject **nameobj, char *format, ...)
393+
{
394+
va_list va;
395+
PyObject *args, *func = 0, *retval;
396+
PyObject *dummy_str = NULL;
397+
va_start(va, format);
398+
399+
func = lookup_maybe(o, name, &dummy_str);
346400
Py_XDECREF(dummy_str);
347401
if (func == NULL) {
348402
va_end(va);
349-
PyErr_SetString(PyExc_AttributeError, name);
403+
if (!PyErr_Occurred()) {
404+
Py_INCREF(Py_NotImplemented);
405+
return Py_NotImplemented;
406+
}
350407
return NULL;
351408
}
352409

@@ -2447,7 +2504,7 @@ FUNCNAME(PyObject *self, PyObject *other) \
24472504
if (self->ob_type->tp_as_number != NULL && \
24482505
self->ob_type->tp_as_number->SLOTNAME == TESTFUNC) { \
24492506
PyObject *r; \
2450-
r = call_method( \
2507+
r = call_maybe( \
24512508
self, OPSTR, &cache_str, "(O)", other); \
24522509
if (r != Py_NotImplemented || \
24532510
other->ob_type == self->ob_type) \
@@ -2456,7 +2513,7 @@ FUNCNAME(PyObject *self, PyObject *other) \
24562513
} \
24572514
if (other->ob_type->tp_as_number != NULL && \
24582515
other->ob_type->tp_as_number->SLOTNAME == TESTFUNC) { \
2459-
return call_method( \
2516+
return call_maybe( \
24602517
other, ROPSTR, &rcache_str, "(O)", self); \
24612518
} \
24622519
Py_INCREF(Py_NotImplemented); \

0 commit comments

Comments
 (0)