How to set docstrings for extensions supporting PyNumberMethods?
Ziga Seilnacht
ziga.seilnacht at gmail.com
Sun Mar 4 03:14:23 EST 2007
Nick Alexander wrote:
> 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:
>>> import silly
>>> print silly.SillyNumber.__add__.__doc__
Add two SillyNumbers.
>>> print silly.SillyNumber.__radd__.__doc__
Same as __add__.
>>> n1 = silly.SillyNumber(3)
>>> n2 = silly.SillyNumber(3.14)
>>> type(n1 + n2) is silly.SillyNumber
True
HTH,
Ziga
More information about the Python-list
mailing list