Defining *class* methods on C code

T

Thomas Heller

I once knew how to do it, but I cannot find or remember it anymore:

How can I attach *class* methods to a type in C code, when I have a
function

PyObject *func(PyObject *type, PyObject *arg);

with the METH_O calling convention? The type is already created, I
cannot insert it into the tp_methods array anymore.

Something like this:

class X(object):
pass

def func(cls, arg):
....

X.func = classmethod(func)

Thanks,

Thomas
 
D

Diez B. Roggisch

How can I attach *class* methods to a type in C code, when I have a
function

PyObject *func(PyObject *type, PyObject *arg);

with the METH_O calling convention? The type is already created, I
cannot insert it into the tp_methods array anymore.

Something like this:

class X(object):
pass

def func(cls, arg):
....

X.func = classmethod(func)

Try METH_STATIC as flag for PyMethodDef.
 
M

Michael Hudson

Thomas Heller said:
I once knew how to do it, but I cannot find or remember it anymore:

How can I attach *class* methods to a type in C code, when I have a
function

PyObject *func(PyObject *type, PyObject *arg);

with the METH_O calling convention? The type is already created, I
cannot insert it into the tp_methods array anymore.

Something like this:

class X(object):
pass

def func(cls, arg):
....

X.func = classmethod(func)

Just stuff it into tp_dict? You'll need to make the method object
yourself, but that's easy.

I think you probably have to hope that the name of the class method
isn't a special method name (you'd want to call
typeobject.c:update_slots then, but I don't think you can arrange for
that to happen from outside typeobject.c as all the juicy symbols are
static).

Or just stick TP_HEAPTYPE into tp_flags, use PyObject_SetAttr and take
TP_HEAPTYPE out again (that's very sick, though).

Cheers,
mwh

--
> Well, as an American citizen I hope that the EU tells the MPAA
> and RIAA to shove it where the Sun don't shine.
Actually they already did. Only first they bent over and dropped
their trousers. -- Shmuel (Seymour J.) Metz & Toni Lassila, asr
 
T

Thomas Heller

Michael Hudson said:
Just stuff it into tp_dict? You'll need to make the method object
yourself, but that's easy.

I think you probably have to hope that the name of the class method
isn't a special method name (you'd want to call
typeobject.c:update_slots then, but I don't think you can arrange for
that to happen from outside typeobject.c as all the juicy symbols are
static).

No special names. Here's the code:

static PyObject *my_method(PyObject *self, PyObject *arg)
{
Py_INCREF(arg);
return arg;
}

static PyMethodDef my_methods[] = {
{ "my_method", my_method, METH_O },
{ NULL, NULL },
};

and then ('type' is the type where I want to create the class method on):

if (somecondition) {
PyObject *func;
PyObject *meth;
PyMethodDef *ml = my_methods;

for (; ml->ml_name; ++ml) {
func = PyCFunction_New(ml, NULL);
if (!func)
return NULL;
meth = PyClassMethod_New(func);
if (!meth)
return NULL;
if (-1 == PyDict_SetItemString(result->tp_dict,
ml->ml_name,
meth))
return NULL;
}
}

I know that there are refcount leaks in this snippet, but that's not the
point.

Trying out the method:

c:\sf\ctypes>py23 -c "from ctypes import *; print c_int.my_method()"
<class 'ctypes.c_int'>

c:\sf\ctypes>py23 -c "from ctypes import *; print c_int.my_method(0)"
Traceback (most recent call last):
File "<string>", line 1, in ?
TypeError: my_method() takes exactly one argument (2 given)
c:\sf\ctypes>

Works *nearly*, but I would have expected that the method accepts one
parameter, pass it as 'arg' to the C function, plus the type itself as
'self'.
Or just stick TP_HEAPTYPE into tp_flags, use PyObject_SetAttr and take
TP_HEAPTYPE out again (that's very sick, though).

Um, why that?

Thanks,

Thomas
 
M

Michael Hudson

Thomas Heller said:
Michael Hudson said:
Just stuff it into tp_dict? You'll need to make the method object
yourself, but that's easy.

I think you probably have to hope that the name of the class method
isn't a special method name (you'd want to call
typeobject.c:update_slots then, but I don't think you can arrange for
that to happen from outside typeobject.c as all the juicy symbols are
static).

No special names. Here's the code:

static PyObject *my_method(PyObject *self, PyObject *arg)
{
Py_INCREF(arg);
return arg;
}

static PyMethodDef my_methods[] = {
{ "my_method", my_method, METH_O },
{ NULL, NULL },
};

and then ('type' is the type where I want to create the class method on):

if (somecondition) {
PyObject *func;
PyObject *meth;
PyMethodDef *ml = my_methods;

for (; ml->ml_name; ++ml) {
func = PyCFunction_New(ml, NULL);
if (!func)
return NULL;
meth = PyClassMethod_New(func);

How about "meth = PyDescr_NewClassMethod(result, ml);" here?
if (!meth)
return NULL;
if (-1 == PyDict_SetItemString(result->tp_dict,
ml->ml_name,
meth))
return NULL;
}
}

I know that there are refcount leaks in this snippet, but that's not the
point.

Trying out the method:

c:\sf\ctypes>py23 -c "from ctypes import *; print c_int.my_method()"
<class 'ctypes.c_int'>

c:\sf\ctypes>py23 -c "from ctypes import *; print c_int.my_method(0)"
Traceback (most recent call last):
File "<string>", line 1, in ?
TypeError: my_method() takes exactly one argument (2 given)
c:\sf\ctypes>

Works *nearly*, but I would have expected that the method accepts one
parameter, pass it as 'arg' to the C function, plus the type itself as
'self'.

Oh... I wouldn't have :)
Um, why that?

Well, if "my_method" was spelt "__add__", say, you'd want to call
typeobject.c:update_slots() to get the function into the
tp_as_number.nb_add fields of the type and all the subclasses. The
hack I outline is one way of doing that.

Cheers,
mwh
 
T

Thomas Heller

[followup to myself, I found it]
Michael Hudson said:
Just stuff it into tp_dict? You'll need to make the method object
yourself, but that's easy.

I think you probably have to hope that the name of the class method
isn't a special method name (you'd want to call
typeobject.c:update_slots then, but I don't think you can arrange for
that to happen from outside typeobject.c as all the juicy symbols are
static).

No special names. Here's the code:

static PyObject *my_method(PyObject *self, PyObject *arg)
{
Py_INCREF(arg);
return arg;
}

static PyMethodDef my_methods[] = {
{ "my_method", my_method, METH_O },
{ NULL, NULL },
};

and then ('type' is the type where I want to create the class method on):
[snipped non-working code]

I have to call PyDescr_NewClassMethodType:

if (somecondition) {
PyObject *meth;
PyMethodDef *ml = my_methods;

for (; ml->ml_name; ++ml) {
meth = PyDescr_NewClassMethod(type, ml);
if (!meth)
return NULL;
if (-1 == PyDict_SetItemString(type->tp_dict,
ml->ml_name,
meth))
return NULL;
}
}
I know that there are refcount leaks in this snippet, but that's not the
point.

Thomas
 
M

Michael Hudson

Thomas Heller said:
Yes, that works. But it's not in Python 2.2, which is required ;-(

Arghl. I guess you can just steal the relavent code... wasn't it your
patch in the first place that implemented all this?

Cheers,
mwh
 
T

Thomas Heller

Michael Hudson said:
Arghl. I guess you can just steal the relavent code... wasn't it your
patch in the first place that implemented all this?

No, it was my feature request. IIRC, it was implemented by Fred Drake.

Thomas
 
T

Thomas Heller

Andrew MacIntyre said:
File a doc bug? I can't find this in the 2.3.3 docs at all...

I've checked it in directly, but only the function prototype.

Thomas
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,768
Messages
2,569,575
Members
45,053
Latest member
billing-software

Latest Threads

Top