How to set docstrings for extensions supporting PyNumberMethods?

N

Nick Alexander

Hello,

I am writing a python extension (compiled C code) that defines an
extension type with PyNumberMethods. Everything works swimmingly,
except I can't deduce a clean way to set the docstring for tp_*
methods. That is, I always have

type.__long__.__doc__ == 'x.__long__() <==> long(x)'

which a quick glance at the Python 2.5 source shows is the default.

I have found that I can use PyObject_GetAttr and PyWrapperDescrObject
and set the descriptor objects d_base->doc to a char pointer... but I
can't tell if this is safe. Or the right way to do it.

If I'm on the wrong list, please let me know!
Thanks,
Nick Alexander
 
Z

Ziga Seilnacht

Nick said:
Hello,

I am writing a python extension (compiled C code) that defines an
extension type with PyNumberMethods. Everything works swimmingly,
except I can't deduce a clean way to set the docstring for tp_*
methods. That is, I always have

type.__long__.__doc__ == 'x.__long__() <==> long(x)'

which a quick glance at the Python 2.5 source shows is the default.

I have found that I can use PyObject_GetAttr and PyWrapperDescrObject
and set the descriptor objects d_base->doc to a char pointer... but I
can't tell if this is safe. Or the right way to do it.

If I'm on the wrong list, please let me know!
Thanks,
Nick Alexander

I think that the right way is to add the methods to the tp_methods
slot and use METH_COEXIST in the PyMethodDef flags field. Example:

/* start of silly module */
#include "Python.h"

typedef struct {
PyObject_HEAD
double value;
} SillyNumber_Object;

/* Forward declarations */
static PyTypeObject SillyNumber_Type;

#define SillyNumber_Check(op) PyObject_TypeCheck(op,
&SillyNumber_Type)

static PyObject *
new_SillyNumber(PyTypeObject *type, double value)
{
PyObject *self;

self = type->tp_alloc(type, 0);
if (self == NULL)
return NULL;

((SillyNumber_Object *)self)->value = value;
return self;
}

static PyObject *
SillyNumber_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
double value = 0.0;
static char *kwlist[] = {"value", 0};

if (!PyArg_ParseTupleAndKeywords(args, kwds, "|d:SillyNumber",
kwlist, &value))
return NULL;

return new_SillyNumber(type, value);
}

static PyObject *
SillyNumber_add(PyObject *left, PyObject *right)
{
double sum;

if (!SillyNumber_Check(left) || !SillyNumber_Check(right)) {
Py_INCREF(Py_NotImplemented);
return Py_NotImplemented;
}

sum = (((SillyNumber_Object *)left)->value +
((SillyNumber_Object *)right)->value);

return new_SillyNumber(&SillyNumber_Type, sum);
}

static PyObject *
SillyNumber_radd(PyObject *right, PyObject *left)
{
return SillyNumber_add(left, right);
}

static PyNumberMethods SillyNumber_as_number = {
SillyNumber_add, /* nb_add */
0, /* nb_subtract */
0, /* nb_multiply */
0, /* nb_divide */
0, /* nb_remainder */
0, /* nb_divmod */
0, /* nb_power */
0, /* nb_negative */
0, /* nb_positive */
0, /* nb_absolute */
0, /* nb_nonzero */
};

static PyMethodDef SillyNumber_methods[] = {
{"__add__", SillyNumber_add, METH_O | METH_COEXIST,
"Add two SillyNumbers."},
{"__radd__", SillyNumber_radd, METH_O | METH_COEXIST,
"Same as __add__."},
{NULL, NULL, 0, NULL}
};

static PyTypeObject SillyNumber_Type = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
"silly.SillyNumber", /* tp_name */
sizeof(SillyNumber_Object), /* tp_basicsize */
0, /* tp_itemsize */
0, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
&SillyNumber_as_number, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | /* tp_flags */
Py_TPFLAGS_CHECKTYPES | /* PyNumberMethods do their own
coercion */
Py_TPFLAGS_BASETYPE, /* SillyNumber_Type allows subclassing
*/
"Silly float numbers", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
SillyNumber_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
SillyNumber_new, /* tp_new */
0, /* tp_free */
};

static PyMethodDef silly_methods[] = {
{NULL, NULL, 0, NULL}
};

PyMODINIT_FUNC
initsilly(void)
{
PyObject *module;

module = Py_InitModule3("silly", silly_methods,
"silly floating number type");
if (module == NULL)
return;

if (PyType_Ready(&SillyNumber_Type) < 0)
return;

Py_INCREF(&SillyNumber_Type);
PyModule_AddObject(module, "SillyNumber", (PyObject *)
&SillyNumber_Type);
}

/* end of silly module */


Results from the interactive session:
True

HTH,
Ziga
 
N

Nick Alexander

NickAlexanderwrote:






I think that the right way is to add the methods to the tp_methods
slot and use METH_COEXIST in the PyMethodDef flags field. Example:

Ziga, thanks for an extremely helpful reply. I'll experiment and post
again if there are issues.

Cheers!
Nick
 

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

No members online now.

Forum statistics

Threads
474,470
Messages
2,571,809
Members
48,797
Latest member
PeterSimpson
Top