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

Skip to content

Commit a3534a6

Browse files
committed
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)*.
1 parent fc5aa9d commit a3534a6

4 files changed

Lines changed: 312 additions & 7 deletions

File tree

Doc/c-api/concrete.rst

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2522,6 +2522,47 @@ There are a few functions specific to Python functions.
25222522
Raises :exc:`SystemError` and returns ``-1`` on failure.
25232523

25242524

2525+
.. _instancemethod-objects:
2526+
2527+
Instance Method Objects
2528+
-----------------------
2529+
2530+
.. index:: object: instancemethod
2531+
2532+
An instance method is a wrapper for a :cdata:`PyCFunction` and the new way
2533+
to bind a :cdata:`PyCFunction` to a class object. It replaces the former call
2534+
:cfunc:`PyMethod_New(func, NULL, class)`.
2535+
2536+
2537+
.. cvar:: PyTypeObject PyInstanceMethod_Type
2538+
2539+
This instance of :ctype:`PyTypeObject` represents the Python instance
2540+
method type. It is not exposed to Python programs.
2541+
2542+
2543+
.. cfunction:: int PyInstanceMethod_Check(PyObject *o)
2544+
2545+
Return true if *o* is an instance method object (has type
2546+
:cdata:`PyInstanceMethod_Type`). The parameter must not be *NULL*.
2547+
2548+
2549+
.. cfunction:: PyObject* PyInstanceMethod_New(PyObject *func)
2550+
2551+
Return a new instance method object, with *func* being any callable object
2552+
*func* is is the function that will be called when the instance method is
2553+
called.
2554+
2555+
2556+
.. cfunction:: PyObject* PyInstanceMethod_Function(PyObject *im)
2557+
2558+
Return the function object associated with the instance method *im*.
2559+
2560+
2561+
.. cfunction:: PyObject* PyInstanceMethod_GET_FUNCTION(PyObject *im)
2562+
2563+
Macro version of :cfunc:`PyInstanceMethod_Function` which avoids error checking.
2564+
2565+
25252566
.. _method-objects:
25262567

25272568
Method Objects

Include/classobject.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,24 @@ PyAPI_FUNC(PyObject *) PyMethod_Self(PyObject *);
3131
#define PyMethod_GET_SELF(meth) \
3232
(((PyMethodObject *)meth) -> im_self)
3333

34+
35+
typedef struct {
36+
PyObject_HEAD
37+
PyObject *func;
38+
} PyInstanceMethodObject;
39+
40+
PyAPI_DATA(PyTypeObject) PyInstanceMethod_Type;
41+
42+
#define PyInstanceMethod_Check(op) ((op)->ob_type == &PyInstanceMethod_Type)
43+
44+
PyAPI_FUNC(PyObject *) PyInstanceMethod_New(PyObject *);
45+
PyAPI_FUNC(PyObject *) PyInstanceMethod_Function(PyObject *);
46+
47+
/* Macros for direct access to these values. Type checks are *not*
48+
done, so use with care. */
49+
#define PyInstanceMethod_GET_FUNCTION(meth) \
50+
(((PyInstanceMethodObject *)meth) -> func)
51+
3452
#ifdef __cplusplus
3553
}
3654
#endif

Misc/NEWS

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ What's New in Python 3.0a3?
1212
Core and Builtins
1313
-----------------
1414

15+
- Issue #1587: Added instancemethod wrapper for PyCFunctions. The Python C API
16+
has gained a new type *PyInstanceMethod_Type* and the functions
17+
*PyInstanceMethod_Check(o)*, *PyInstanceMethod_New(func)* and
18+
*PyInstanceMethod_Function(im)*.
19+
1520
- Constants gc.DEBUG_OBJECT and gc.DEBUG_INSTANCE have been removed from the
1621
gc module; gc.DEBUG_COLLECTABLE or gc.DEBUG_UNCOLLECTABLE are now enough to
1722
print the corresponding list of objects considered by the garbage collector.

Objects/classobject.c

Lines changed: 248 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
#define TP_DESCR_GET(t) ((t)->tp_descr_get)
77

8-
98
PyObject *
109
PyMethod_Function(PyObject *im)
1110
{
@@ -68,12 +67,12 @@ PyMethod_New(PyObject *func, PyObject *self)
6867

6968
/* im_func and im_self are stored in the PyMethod object */
7069

71-
#define OFF(x) offsetof(PyMethodObject, x)
70+
#define MO_OFF(x) offsetof(PyMethodObject, x)
7271

7372
static PyMemberDef method_memberlist[] = {
74-
{"__func__", T_OBJECT, OFF(im_func), READONLY|RESTRICTED,
73+
{"__func__", T_OBJECT, MO_OFF(im_func), READONLY|RESTRICTED,
7574
"the function (or other callable) implementing a method"},
76-
{"__self__", T_OBJECT, OFF(im_self), READONLY|RESTRICTED,
75+
{"__self__", T_OBJECT, MO_OFF(im_self), READONLY|RESTRICTED,
7776
"the instance to which a method is bound"},
7877
{NULL} /* Sentinel */
7978
};
@@ -131,15 +130,15 @@ method_getattro(PyObject *obj, PyObject *name)
131130
PyDoc_STRVAR(method_doc,
132131
"method(function, instance)\n\
133132
\n\
134-
Create an instance method object.");
133+
Create a bound instance method object.");
135134

136135
static PyObject *
137136
method_new(PyTypeObject* type, PyObject* args, PyObject *kw)
138137
{
139138
PyObject *func;
140139
PyObject *self;
141140

142-
if (!_PyArg_NoKeywords("instancemethod", kw))
141+
if (!_PyArg_NoKeywords("method", kw))
143142
return NULL;
144143
if (!PyArg_UnpackTuple(args, "method", 2, 2,
145144
&func, &self))
@@ -351,7 +350,7 @@ PyTypeObject PyMethod_Type = {
351350
(traverseproc)method_traverse, /* tp_traverse */
352351
0, /* tp_clear */
353352
method_richcompare, /* tp_richcompare */
354-
offsetof(PyMethodObject, im_weakreflist), /* tp_weaklistoffset */
353+
offsetof(PyMethodObject, im_weakreflist), /* tp_weaklistoffset */
355354
0, /* tp_iter */
356355
0, /* tp_iternext */
357356
0, /* tp_methods */
@@ -378,3 +377,245 @@ PyMethod_Fini(void)
378377
PyObject_GC_Del(im);
379378
}
380379
}
380+
381+
/* ------------------------------------------------------------------------
382+
* instance method
383+
*/
384+
385+
PyObject *
386+
PyInstanceMethod_New(PyObject *func) {
387+
PyInstanceMethodObject *method;
388+
method = PyObject_GC_New(PyInstanceMethodObject,
389+
&PyInstanceMethod_Type);
390+
if (method == NULL) return NULL;
391+
Py_INCREF(func);
392+
method->func = func;
393+
_PyObject_GC_TRACK(method);
394+
return (PyObject *)method;
395+
}
396+
397+
PyObject *
398+
PyInstanceMethod_Function(PyObject *im)
399+
{
400+
if (!PyInstanceMethod_Check(im)) {
401+
PyErr_BadInternalCall();
402+
return NULL;
403+
}
404+
return PyInstanceMethod_GET_FUNCTION(im);
405+
}
406+
407+
#define IMO_OFF(x) offsetof(PyInstanceMethodObject, x)
408+
409+
static PyMemberDef instancemethod_memberlist[] = {
410+
{"__func__", T_OBJECT, IMO_OFF(func), READONLY|RESTRICTED,
411+
"the function (or other callable) implementing a method"},
412+
{NULL} /* Sentinel */
413+
};
414+
415+
static PyObject *
416+
instancemethod_get_doc(PyObject *self, void *context)
417+
{
418+
static PyObject *docstr;
419+
if (docstr == NULL) {
420+
docstr = PyUnicode_InternFromString("__doc__");
421+
if (docstr == NULL)
422+
return NULL;
423+
}
424+
return PyObject_GetAttr(PyInstanceMethod_GET_FUNCTION(self), docstr);
425+
}
426+
427+
static PyGetSetDef instancemethod_getset[] = {
428+
{"__doc__", (getter)instancemethod_get_doc, NULL, NULL},
429+
{0}
430+
};
431+
432+
static PyObject *
433+
instancemethod_getattro(PyObject *self, PyObject *name)
434+
{
435+
PyTypeObject *tp = self->ob_type;
436+
PyObject *descr = NULL;
437+
438+
if (tp->tp_dict == NULL) {
439+
if (PyType_Ready(tp) < 0)
440+
return NULL;
441+
}
442+
descr = _PyType_Lookup(tp, name);
443+
444+
if (descr != NULL) {
445+
descrgetfunc f = TP_DESCR_GET(descr->ob_type);
446+
if (f != NULL)
447+
return f(descr, self, (PyObject *)self->ob_type);
448+
else {
449+
Py_INCREF(descr);
450+
return descr;
451+
}
452+
}
453+
454+
return PyObject_GetAttr(PyInstanceMethod_GET_FUNCTION(self), name);
455+
}
456+
457+
static void
458+
instancemethod_dealloc(PyObject *self) {
459+
_PyObject_GC_UNTRACK(self);
460+
Py_DECREF(PyInstanceMethod_GET_FUNCTION(self));
461+
PyObject_GC_Del(self);
462+
}
463+
464+
static int
465+
instancemethod_traverse(PyObject *self, visitproc visit, void *arg) {
466+
Py_VISIT(PyInstanceMethod_GET_FUNCTION(self));
467+
return 0;
468+
}
469+
470+
static PyObject *
471+
instancemethod_call(PyObject *self, PyObject *arg, PyObject *kw)
472+
{
473+
return PyObject_Call(PyMethod_GET_FUNCTION(self), arg, kw);
474+
}
475+
476+
static PyObject *
477+
instancemethod_descr_get(PyObject *descr, PyObject *obj, PyObject *type) {
478+
register PyObject *func = PyInstanceMethod_GET_FUNCTION(descr);
479+
if (obj == NULL)
480+
return func;
481+
else
482+
return PyMethod_New(func, obj);
483+
}
484+
485+
static PyObject *
486+
instancemethod_richcompare(PyObject *self, PyObject *other, int op)
487+
{
488+
PyInstanceMethodObject *a, *b;
489+
PyObject *res;
490+
int eq;
491+
492+
if ((op != Py_EQ && op != Py_NE) ||
493+
!PyInstanceMethod_Check(self) ||
494+
!PyInstanceMethod_Check(other))
495+
{
496+
Py_INCREF(Py_NotImplemented);
497+
return Py_NotImplemented;
498+
}
499+
a = (PyInstanceMethodObject *)self;
500+
b = (PyInstanceMethodObject *)other;
501+
eq = PyObject_RichCompareBool(a->func, b->func, Py_EQ);
502+
if (eq < 0)
503+
return NULL;
504+
if (op == Py_EQ)
505+
res = eq ? Py_True : Py_False;
506+
else
507+
res = eq ? Py_False : Py_True;
508+
Py_INCREF(res);
509+
return res;
510+
}
511+
512+
static PyObject *
513+
instancemethod_repr(PyObject *self)
514+
{
515+
PyObject *func = PyInstanceMethod_Function(self);
516+
PyObject *funcname = NULL , *result = NULL;
517+
char *defname = "?";
518+
519+
if (func == NULL) {
520+
PyErr_BadInternalCall();
521+
return NULL;
522+
}
523+
524+
funcname = PyObject_GetAttrString(func, "__name__");
525+
if (funcname == NULL) {
526+
if (!PyErr_ExceptionMatches(PyExc_AttributeError))
527+
return NULL;
528+
PyErr_Clear();
529+
}
530+
else if (!PyUnicode_Check(funcname)) {
531+
Py_DECREF(funcname);
532+
funcname = NULL;
533+
}
534+
535+
result = PyUnicode_FromFormat("<instancemethod %V at %p>",
536+
funcname, defname, self);
537+
538+
Py_XDECREF(funcname);
539+
return result;
540+
}
541+
542+
/*
543+
static long
544+
instancemethod_hash(PyObject *self)
545+
{
546+
long x, y;
547+
x = (long)self;
548+
y = PyObject_Hash(PyInstanceMethod_GET_FUNCTION(self));
549+
if (y == -1)
550+
return -1;
551+
x = x ^ y;
552+
if (x == -1)
553+
x = -2;
554+
return x;
555+
}
556+
*/
557+
558+
PyDoc_STRVAR(instancemethod_doc,
559+
"instancemethod(function)\n\
560+
\n\
561+
Bind a function to a class.");
562+
563+
static PyObject *
564+
instancemethod_new(PyTypeObject* type, PyObject* args, PyObject *kw)
565+
{
566+
PyObject *func;
567+
568+
if (!_PyArg_NoKeywords("instancemethod", kw))
569+
return NULL;
570+
if (!PyArg_UnpackTuple(args, "instancemethod", 1, 1, &func))
571+
return NULL;
572+
if (!PyCallable_Check(func)) {
573+
PyErr_SetString(PyExc_TypeError,
574+
"first argument must be callable");
575+
return NULL;
576+
}
577+
578+
return PyInstanceMethod_New(func);
579+
}
580+
581+
PyTypeObject PyInstanceMethod_Type = {
582+
PyVarObject_HEAD_INIT(&PyType_Type, 0)
583+
"instancemethod", /* tp_name */
584+
sizeof(PyInstanceMethodObject), /* tp_basicsize */
585+
0, /* tp_itemsize */
586+
instancemethod_dealloc, /* tp_dealloc */
587+
0, /* tp_print */
588+
0, /* tp_getattr */
589+
0, /* tp_setattr */
590+
0, /* tp_compare */
591+
(reprfunc)instancemethod_repr, /* tp_repr */
592+
0, /* tp_as_number */
593+
0, /* tp_as_sequence */
594+
0, /* tp_as_mapping */
595+
0, /*(hashfunc)instancemethod_hash, tp_hash */
596+
instancemethod_call, /* tp_call */
597+
0, /* tp_str */
598+
instancemethod_getattro, /* tp_getattro */
599+
PyObject_GenericSetAttr, /* tp_setattro */
600+
0, /* tp_as_buffer */
601+
Py_TPFLAGS_DEFAULT
602+
| Py_TPFLAGS_HAVE_GC, /* tp_flags */
603+
instancemethod_doc, /* tp_doc */
604+
instancemethod_traverse, /* tp_traverse */
605+
0, /* tp_clear */
606+
instancemethod_richcompare, /* tp_richcompare */
607+
0, /* tp_weaklistoffset */
608+
0, /* tp_iter */
609+
0, /* tp_iternext */
610+
0, /* tp_methods */
611+
instancemethod_memberlist, /* tp_members */
612+
instancemethod_getset, /* tp_getset */
613+
0, /* tp_base */
614+
0, /* tp_dict */
615+
instancemethod_descr_get, /* tp_descr_get */
616+
0, /* tp_descr_set */
617+
0, /* tp_dictoffset */
618+
0, /* tp_init */
619+
0, /* tp_alloc */
620+
instancemethod_new, /* tp_new */
621+
};

0 commit comments

Comments
 (0)