summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/c-api/concrete.rst41
-rw-r--r--Include/classobject.h18
-rw-r--r--Misc/NEWS5
-rw-r--r--Objects/classobject.c255
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("<instancemethod %V at %p>",
+ 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 */
+};