Commit f21c6be7 authored by Guido van Rossum's avatar Guido van Rossum

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).
parent f2a5f3f7
...@@ -300,15 +300,23 @@ PyType_IsSubtype(PyTypeObject *a, PyTypeObject *b) ...@@ -300,15 +300,23 @@ PyType_IsSubtype(PyTypeObject *a, PyTypeObject *b)
} }
} }
/* Internal routine to do a method lookup in the type /* Internal routines to do a method lookup in the type
without looking in the instance dictionary without looking in the instance dictionary
(so we can't use PyObject_GetAttr) but still binding (so we can't use PyObject_GetAttr) but still binding
it to the instance. The arguments are the object, it to the instance. The arguments are the object,
the method name as a C string, and the address of a the method name as a C string, and the address of a
static variable used to cache the interned Python string. */ static variable used to cache the interned Python string.
Two variants:
- lookup_maybe() returns NULL without raising an exception
when the _PyType_Lookup() call fails;
- lookup_method() always raises an exception upon errors.
*/
static PyObject * static PyObject *
lookup_method(PyObject *self, char *attrstr, PyObject **attrobj) lookup_maybe(PyObject *self, char *attrstr, PyObject **attrobj)
{ {
PyObject *res; PyObject *res;
...@@ -318,9 +326,7 @@ lookup_method(PyObject *self, char *attrstr, PyObject **attrobj) ...@@ -318,9 +326,7 @@ lookup_method(PyObject *self, char *attrstr, PyObject **attrobj)
return NULL; return NULL;
} }
res = _PyType_Lookup(self->ob_type, *attrobj); res = _PyType_Lookup(self->ob_type, *attrobj);
if (res == NULL) if (res != NULL) {
PyErr_SetObject(PyExc_AttributeError, *attrobj);
else {
descrgetfunc f; descrgetfunc f;
if ((f = res->ob_type->tp_descr_get) == NULL) if ((f = res->ob_type->tp_descr_get) == NULL)
Py_INCREF(res); Py_INCREF(res);
...@@ -330,6 +336,15 @@ lookup_method(PyObject *self, char *attrstr, PyObject **attrobj) ...@@ -330,6 +336,15 @@ lookup_method(PyObject *self, char *attrstr, PyObject **attrobj)
return res; return res;
} }
static PyObject *
lookup_method(PyObject *self, char *attrstr, PyObject **attrobj)
{
PyObject *res = lookup_maybe(self, attrstr, attrobj);
if (res == NULL && !PyErr_Occurred())
PyErr_SetObject(PyExc_AttributeError, *attrobj);
return res;
}
/* A variation of PyObject_CallMethod that uses lookup_method() /* A variation of PyObject_CallMethod that uses lookup_method()
instead of PyObject_GetAttrString(). This uses the same convention instead of PyObject_GetAttrString(). This uses the same convention
as lookup_method to cache the interned name string object. */ as lookup_method to cache the interned name string object. */
...@@ -342,11 +357,53 @@ call_method(PyObject *o, char *name, PyObject **nameobj, char *format, ...) ...@@ -342,11 +357,53 @@ call_method(PyObject *o, char *name, PyObject **nameobj, char *format, ...)
PyObject *dummy_str = NULL; PyObject *dummy_str = NULL;
va_start(va, format); va_start(va, format);
func = lookup_method(o, name, &dummy_str); func = lookup_maybe(o, name, &dummy_str);
if (func == NULL) {
va_end(va);
if (!PyErr_Occurred())
PyErr_SetObject(PyExc_AttributeError, dummy_str);
Py_XDECREF(dummy_str);
return NULL;
}
Py_DECREF(dummy_str);
if (format && *format)
args = Py_VaBuildValue(format, va);
else
args = PyTuple_New(0);
va_end(va);
if (args == NULL)
return NULL;
assert(PyTuple_Check(args));
retval = PyObject_Call(func, args, NULL);
Py_DECREF(args);
Py_DECREF(func);
return retval;
}
/* Clone of call_method() that returns NotImplemented when the lookup fails. */
PyObject *
call_maybe(PyObject *o, char *name, PyObject **nameobj, char *format, ...)
{
va_list va;
PyObject *args, *func = 0, *retval;
PyObject *dummy_str = NULL;
va_start(va, format);
func = lookup_maybe(o, name, &dummy_str);
Py_XDECREF(dummy_str); Py_XDECREF(dummy_str);
if (func == NULL) { if (func == NULL) {
va_end(va); va_end(va);
PyErr_SetString(PyExc_AttributeError, name); if (!PyErr_Occurred()) {
Py_INCREF(Py_NotImplemented);
return Py_NotImplemented;
}
return NULL; return NULL;
} }
...@@ -2447,7 +2504,7 @@ FUNCNAME(PyObject *self, PyObject *other) \ ...@@ -2447,7 +2504,7 @@ FUNCNAME(PyObject *self, PyObject *other) \
if (self->ob_type->tp_as_number != NULL && \ if (self->ob_type->tp_as_number != NULL && \
self->ob_type->tp_as_number->SLOTNAME == TESTFUNC) { \ self->ob_type->tp_as_number->SLOTNAME == TESTFUNC) { \
PyObject *r; \ PyObject *r; \
r = call_method( \ r = call_maybe( \
self, OPSTR, &cache_str, "(O)", other); \ self, OPSTR, &cache_str, "(O)", other); \
if (r != Py_NotImplemented || \ if (r != Py_NotImplemented || \
other->ob_type == self->ob_type) \ other->ob_type == self->ob_type) \
...@@ -2456,7 +2513,7 @@ FUNCNAME(PyObject *self, PyObject *other) \ ...@@ -2456,7 +2513,7 @@ FUNCNAME(PyObject *self, PyObject *other) \
} \ } \
if (other->ob_type->tp_as_number != NULL && \ if (other->ob_type->tp_as_number != NULL && \
other->ob_type->tp_as_number->SLOTNAME == TESTFUNC) { \ other->ob_type->tp_as_number->SLOTNAME == TESTFUNC) { \
return call_method( \ return call_maybe( \
other, ROPSTR, &rcache_str, "(O)", self); \ other, ROPSTR, &rcache_str, "(O)", self); \
} \ } \
Py_INCREF(Py_NotImplemented); \ Py_INCREF(Py_NotImplemented); \
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment