From a3534a6ff5602ea848b1d27f4f9d9d7913cbe31b Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Tue, 11 Dec 2007 19:56:40 +0000 Subject: Issue #1587: Added instancemethod wrapper for PyCFunctions. The Python C API has gained a new type *PyInstanceMethod_Type* and the functions *PyInstanceMethod_Check(o)*, *PyInstanceMethod_New(func)* and *PyInstanceMethod_Function(im)*. --- Doc/c-api/concrete.rst | 41 ++++++++ Include/classobject.h | 18 ++++ Misc/NEWS | 5 + Objects/classobject.c | 255 +++++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 312 insertions(+), 7 deletions(-) diff --git a/Doc/c-api/concrete.rst b/Doc/c-api/concrete.rst index 5ff8969..b293ebf 100644 --- a/Doc/c-api/concrete.rst +++ b/Doc/c-api/concrete.rst @@ -2522,6 +2522,47 @@ There are a few functions specific to Python functions. Raises :exc:`SystemError` and returns ``-1`` on failure. +.. _instancemethod-objects: + +Instance Method Objects +----------------------- + +.. index:: object: instancemethod + +An instance method is a wrapper for a :cdata:`PyCFunction` and the new way +to bind a :cdata:`PyCFunction` to a class object. It replaces the former call +:cfunc:`PyMethod_New(func, NULL, class)`. + + +.. cvar:: PyTypeObject PyInstanceMethod_Type + + This instance of :ctype:`PyTypeObject` represents the Python instance + method type. It is not exposed to Python programs. + + +.. cfunction:: int PyInstanceMethod_Check(PyObject *o) + + Return true if *o* is an instance method object (has type + :cdata:`PyInstanceMethod_Type`). The parameter must not be *NULL*. + + +.. cfunction:: PyObject* PyInstanceMethod_New(PyObject *func) + + Return a new instance method object, with *func* being any callable object + *func* is is the function that will be called when the instance method is + called. + + +.. cfunction:: PyObject* PyInstanceMethod_Function(PyObject *im) + + Return the function object associated with the instance method *im*. + + +.. cfunction:: PyObject* PyInstanceMethod_GET_FUNCTION(PyObject *im) + + Macro version of :cfunc:`PyInstanceMethod_Function` which avoids error checking. + + .. _method-objects: Method Objects diff --git a/Include/classobject.h b/Include/classobject.h index 699546e..f6789d1 100644 --- a/Include/classobject.h +++ b/Include/classobject.h @@ -31,6 +31,24 @@ PyAPI_FUNC(PyObject *) PyMethod_Self(PyObject *); #define PyMethod_GET_SELF(meth) \ (((PyMethodObject *)meth) -> im_self) + +typedef struct { + PyObject_HEAD + PyObject *func; +} PyInstanceMethodObject; + +PyAPI_DATA(PyTypeObject) PyInstanceMethod_Type; + +#define PyInstanceMethod_Check(op) ((op)->ob_type == &PyInstanceMethod_Type) + +PyAPI_FUNC(PyObject *) PyInstanceMethod_New(PyObject *); +PyAPI_FUNC(PyObject *) PyInstanceMethod_Function(PyObject *); + +/* Macros for direct access to these values. Type checks are *not* + done, so use with care. */ +#define PyInstanceMethod_GET_FUNCTION(meth) \ + (((PyInstanceMethodObject *)meth) -> func) + #ifdef __cplusplus } #endif diff --git a/Misc/NEWS b/Misc/NEWS index afff9fb..7516516 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -12,6 +12,11 @@ What's New in Python 3.0a3? Core and Builtins ----------------- +- Issue #1587: Added instancemethod wrapper for PyCFunctions. The Python C API + has gained a new type *PyInstanceMethod_Type* and the functions + *PyInstanceMethod_Check(o)*, *PyInstanceMethod_New(func)* and + *PyInstanceMethod_Function(im)*. + - Constants gc.DEBUG_OBJECT and gc.DEBUG_INSTANCE have been removed from the gc module; gc.DEBUG_COLLECTABLE or gc.DEBUG_UNCOLLECTABLE are now enough to print the corresponding list of objects considered by the garbage collector. diff --git a/Objects/classobject.c b/Objects/classobject.c index 5500ed5..e387142 100644 --- a/Objects/classobject.c +++ b/Objects/classobject.c @@ -5,7 +5,6 @@ #define TP_DESCR_GET(t) ((t)->tp_descr_get) - PyObject * PyMethod_Function(PyObject *im) { @@ -68,12 +67,12 @@ PyMethod_New(PyObject *func, PyObject *self) /* im_func and im_self are stored in the PyMethod object */ -#define OFF(x) offsetof(PyMethodObject, x) +#define MO_OFF(x) offsetof(PyMethodObject, x) static PyMemberDef method_memberlist[] = { - {"__func__", T_OBJECT, OFF(im_func), READONLY|RESTRICTED, + {"__func__", T_OBJECT, MO_OFF(im_func), READONLY|RESTRICTED, "the function (or other callable) implementing a method"}, - {"__self__", T_OBJECT, OFF(im_self), READONLY|RESTRICTED, + {"__self__", T_OBJECT, MO_OFF(im_self), READONLY|RESTRICTED, "the instance to which a method is bound"}, {NULL} /* Sentinel */ }; @@ -131,7 +130,7 @@ method_getattro(PyObject *obj, PyObject *name) PyDoc_STRVAR(method_doc, "method(function, instance)\n\ \n\ -Create an instance method object."); +Create a bound instance method object."); static PyObject * method_new(PyTypeObject* type, PyObject* args, PyObject *kw) @@ -139,7 +138,7 @@ method_new(PyTypeObject* type, PyObject* args, PyObject *kw) PyObject *func; PyObject *self; - if (!_PyArg_NoKeywords("instancemethod", kw)) + if (!_PyArg_NoKeywords("method", kw)) return NULL; if (!PyArg_UnpackTuple(args, "method", 2, 2, &func, &self)) @@ -351,7 +350,7 @@ PyTypeObject PyMethod_Type = { (traverseproc)method_traverse, /* tp_traverse */ 0, /* tp_clear */ method_richcompare, /* tp_richcompare */ - offsetof(PyMethodObject, im_weakreflist), /* tp_weaklistoffset */ + offsetof(PyMethodObject, im_weakreflist), /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ @@ -378,3 +377,245 @@ PyMethod_Fini(void) PyObject_GC_Del(im); } } + +/* ------------------------------------------------------------------------ + * instance method + */ + +PyObject * +PyInstanceMethod_New(PyObject *func) { + PyInstanceMethodObject *method; + method = PyObject_GC_New(PyInstanceMethodObject, + &PyInstanceMethod_Type); + if (method == NULL) return NULL; + Py_INCREF(func); + method->func = func; + _PyObject_GC_TRACK(method); + return (PyObject *)method; +} + +PyObject * +PyInstanceMethod_Function(PyObject *im) +{ + if (!PyInstanceMethod_Check(im)) { + PyErr_BadInternalCall(); + return NULL; + } + return PyInstanceMethod_GET_FUNCTION(im); +} + +#define IMO_OFF(x) offsetof(PyInstanceMethodObject, x) + +static PyMemberDef instancemethod_memberlist[] = { + {"__func__", T_OBJECT, IMO_OFF(func), READONLY|RESTRICTED, + "the function (or other callable) implementing a method"}, + {NULL} /* Sentinel */ +}; + +static PyObject * +instancemethod_get_doc(PyObject *self, void *context) +{ + static PyObject *docstr; + if (docstr == NULL) { + docstr = PyUnicode_InternFromString("__doc__"); + if (docstr == NULL) + return NULL; + } + return PyObject_GetAttr(PyInstanceMethod_GET_FUNCTION(self), docstr); +} + +static PyGetSetDef instancemethod_getset[] = { + {"__doc__", (getter)instancemethod_get_doc, NULL, NULL}, + {0} +}; + +static PyObject * +instancemethod_getattro(PyObject *self, PyObject *name) +{ + PyTypeObject *tp = self->ob_type; + PyObject *descr = NULL; + + if (tp->tp_dict == NULL) { + if (PyType_Ready(tp) < 0) + return NULL; + } + descr = _PyType_Lookup(tp, name); + + if (descr != NULL) { + descrgetfunc f = TP_DESCR_GET(descr->ob_type); + if (f != NULL) + return f(descr, self, (PyObject *)self->ob_type); + else { + Py_INCREF(descr); + return descr; + } + } + + return PyObject_GetAttr(PyInstanceMethod_GET_FUNCTION(self), name); +} + +static void +instancemethod_dealloc(PyObject *self) { + _PyObject_GC_UNTRACK(self); + Py_DECREF(PyInstanceMethod_GET_FUNCTION(self)); + PyObject_GC_Del(self); +} + +static int +instancemethod_traverse(PyObject *self, visitproc visit, void *arg) { + Py_VISIT(PyInstanceMethod_GET_FUNCTION(self)); + return 0; +} + +static PyObject * +instancemethod_call(PyObject *self, PyObject *arg, PyObject *kw) +{ + return PyObject_Call(PyMethod_GET_FUNCTION(self), arg, kw); +} + +static PyObject * +instancemethod_descr_get(PyObject *descr, PyObject *obj, PyObject *type) { + register PyObject *func = PyInstanceMethod_GET_FUNCTION(descr); + if (obj == NULL) + return func; + else + return PyMethod_New(func, obj); +} + +static PyObject * +instancemethod_richcompare(PyObject *self, PyObject *other, int op) +{ + PyInstanceMethodObject *a, *b; + PyObject *res; + int eq; + + if ((op != Py_EQ && op != Py_NE) || + !PyInstanceMethod_Check(self) || + !PyInstanceMethod_Check(other)) + { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + a = (PyInstanceMethodObject *)self; + b = (PyInstanceMethodObject *)other; + eq = PyObject_RichCompareBool(a->func, b->func, Py_EQ); + if (eq < 0) + return NULL; + if (op == Py_EQ) + res = eq ? Py_True : Py_False; + else + res = eq ? Py_False : Py_True; + Py_INCREF(res); + return res; +} + +static PyObject * +instancemethod_repr(PyObject *self) +{ + PyObject *func = PyInstanceMethod_Function(self); + PyObject *funcname = NULL , *result = NULL; + char *defname = "?"; + + if (func == NULL) { + PyErr_BadInternalCall(); + return NULL; + } + + funcname = PyObject_GetAttrString(func, "__name__"); + if (funcname == NULL) { + if (!PyErr_ExceptionMatches(PyExc_AttributeError)) + return NULL; + PyErr_Clear(); + } + else if (!PyUnicode_Check(funcname)) { + Py_DECREF(funcname); + funcname = NULL; + } + + result = PyUnicode_FromFormat("", + funcname, defname, self); + + Py_XDECREF(funcname); + return result; +} + +/* +static long +instancemethod_hash(PyObject *self) +{ + long x, y; + x = (long)self; + y = PyObject_Hash(PyInstanceMethod_GET_FUNCTION(self)); + if (y == -1) + return -1; + x = x ^ y; + if (x == -1) + x = -2; + return x; +} +*/ + +PyDoc_STRVAR(instancemethod_doc, +"instancemethod(function)\n\ +\n\ +Bind a function to a class."); + +static PyObject * +instancemethod_new(PyTypeObject* type, PyObject* args, PyObject *kw) +{ + PyObject *func; + + if (!_PyArg_NoKeywords("instancemethod", kw)) + return NULL; + if (!PyArg_UnpackTuple(args, "instancemethod", 1, 1, &func)) + return NULL; + if (!PyCallable_Check(func)) { + PyErr_SetString(PyExc_TypeError, + "first argument must be callable"); + return NULL; + } + + return PyInstanceMethod_New(func); +} + +PyTypeObject PyInstanceMethod_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "instancemethod", /* tp_name */ + sizeof(PyInstanceMethodObject), /* tp_basicsize */ + 0, /* tp_itemsize */ + instancemethod_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)instancemethod_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /*(hashfunc)instancemethod_hash, tp_hash */ + instancemethod_call, /* tp_call */ + 0, /* tp_str */ + instancemethod_getattro, /* tp_getattro */ + PyObject_GenericSetAttr, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_HAVE_GC, /* tp_flags */ + instancemethod_doc, /* tp_doc */ + instancemethod_traverse, /* tp_traverse */ + 0, /* tp_clear */ + instancemethod_richcompare, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + instancemethod_memberlist, /* tp_members */ + instancemethod_getset, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + instancemethod_descr_get, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + instancemethod_new, /* tp_new */ +}; -- cgit v0.12