#include "Python.h" | |
#include "structmember.h" | |
PyDoc_STRVAR(xxsubtype__doc__, | |
"xxsubtype is an example module showing how to subtype builtin types from C.\n" | |
"test_descr.py in the standard test suite requires it in order to complete.\n" | |
"If you don't care about the examples, and don't intend to run the Python\n" | |
"test suite, you can recompile Python without Modules/xxsubtype.c."); | |
/* We link this module statically for convenience. If compiled as a shared | |
library instead, some compilers don't allow addresses of Python objects | |
defined in other libraries to be used in static initializers here. The | |
DEFERRED_ADDRESS macro is used to tag the slots where such addresses | |
appear; the module init function must fill in the tagged slots at runtime. | |
The argument is for documentation -- the macro ignores it. | |
*/ | |
#define DEFERRED_ADDRESS(ADDR) 0 | |
/* spamlist -- a list subtype */ | |
typedef struct { | |
PyListObject list; | |
int state; | |
} spamlistobject; | |
static PyObject * | |
spamlist_getstate(spamlistobject *self, PyObject *args) | |
{ | |
if (!PyArg_ParseTuple(args, ":getstate")) | |
return NULL; | |
return PyInt_FromLong(self->state); | |
} | |
static PyObject * | |
spamlist_setstate(spamlistobject *self, PyObject *args) | |
{ | |
int state; | |
if (!PyArg_ParseTuple(args, "i:setstate", &state)) | |
return NULL; | |
self->state = state; | |
Py_INCREF(Py_None); | |
return Py_None; | |
} | |
static PyObject * | |
spamlist_specialmeth(PyObject *self, PyObject *args, PyObject *kw) | |
{ | |
PyObject *result = PyTuple_New(3); | |
if (result != NULL) { | |
if (self == NULL) | |
self = Py_None; | |
if (kw == NULL) | |
kw = Py_None; | |
Py_INCREF(self); | |
PyTuple_SET_ITEM(result, 0, self); | |
Py_INCREF(args); | |
PyTuple_SET_ITEM(result, 1, args); | |
Py_INCREF(kw); | |
PyTuple_SET_ITEM(result, 2, kw); | |
} | |
return result; | |
} | |
static PyMethodDef spamlist_methods[] = { | |
{"getstate", (PyCFunction)spamlist_getstate, METH_VARARGS, | |
PyDoc_STR("getstate() -> state")}, | |
{"setstate", (PyCFunction)spamlist_setstate, METH_VARARGS, | |
PyDoc_STR("setstate(state)")}, | |
/* These entries differ only in the flags; they are used by the tests | |
in test.test_descr. */ | |
{"classmeth", (PyCFunction)spamlist_specialmeth, | |
METH_VARARGS | METH_KEYWORDS | METH_CLASS, | |
PyDoc_STR("classmeth(*args, **kw)")}, | |
{"staticmeth", (PyCFunction)spamlist_specialmeth, | |
METH_VARARGS | METH_KEYWORDS | METH_STATIC, | |
PyDoc_STR("staticmeth(*args, **kw)")}, | |
{NULL, NULL}, | |
}; | |
static int | |
spamlist_init(spamlistobject *self, PyObject *args, PyObject *kwds) | |
{ | |
if (PyList_Type.tp_init((PyObject *)self, args, kwds) < 0) | |
return -1; | |
self->state = 0; | |
return 0; | |
} | |
static PyObject * | |
spamlist_state_get(spamlistobject *self) | |
{ | |
return PyInt_FromLong(self->state); | |
} | |
static PyGetSetDef spamlist_getsets[] = { | |
{"state", (getter)spamlist_state_get, NULL, | |
PyDoc_STR("an int variable for demonstration purposes")}, | |
{0} | |
}; | |
static PyTypeObject spamlist_type = { | |
PyVarObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type), 0) | |
"xxsubtype.spamlist", | |
sizeof(spamlistobject), | |
0, | |
0, /* tp_dealloc */ | |
0, /* tp_print */ | |
0, /* tp_getattr */ | |
0, /* tp_setattr */ | |
0, /* tp_compare */ | |
0, /* tp_repr */ | |
0, /* 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 | Py_TPFLAGS_BASETYPE, /* tp_flags */ | |
0, /* tp_doc */ | |
0, /* tp_traverse */ | |
0, /* tp_clear */ | |
0, /* tp_richcompare */ | |
0, /* tp_weaklistoffset */ | |
0, /* tp_iter */ | |
0, /* tp_iternext */ | |
spamlist_methods, /* tp_methods */ | |
0, /* tp_members */ | |
spamlist_getsets, /* tp_getset */ | |
DEFERRED_ADDRESS(&PyList_Type), /* tp_base */ | |
0, /* tp_dict */ | |
0, /* tp_descr_get */ | |
0, /* tp_descr_set */ | |
0, /* tp_dictoffset */ | |
(initproc)spamlist_init, /* tp_init */ | |
0, /* tp_alloc */ | |
0, /* tp_new */ | |
}; | |
/* spamdict -- a dict subtype */ | |
typedef struct { | |
PyDictObject dict; | |
int state; | |
} spamdictobject; | |
static PyObject * | |
spamdict_getstate(spamdictobject *self, PyObject *args) | |
{ | |
if (!PyArg_ParseTuple(args, ":getstate")) | |
return NULL; | |
return PyInt_FromLong(self->state); | |
} | |
static PyObject * | |
spamdict_setstate(spamdictobject *self, PyObject *args) | |
{ | |
int state; | |
if (!PyArg_ParseTuple(args, "i:setstate", &state)) | |
return NULL; | |
self->state = state; | |
Py_INCREF(Py_None); | |
return Py_None; | |
} | |
static PyMethodDef spamdict_methods[] = { | |
{"getstate", (PyCFunction)spamdict_getstate, METH_VARARGS, | |
PyDoc_STR("getstate() -> state")}, | |
{"setstate", (PyCFunction)spamdict_setstate, METH_VARARGS, | |
PyDoc_STR("setstate(state)")}, | |
{NULL, NULL}, | |
}; | |
static int | |
spamdict_init(spamdictobject *self, PyObject *args, PyObject *kwds) | |
{ | |
if (PyDict_Type.tp_init((PyObject *)self, args, kwds) < 0) | |
return -1; | |
self->state = 0; | |
return 0; | |
} | |
static PyMemberDef spamdict_members[] = { | |
{"state", T_INT, offsetof(spamdictobject, state), READONLY, | |
PyDoc_STR("an int variable for demonstration purposes")}, | |
{0} | |
}; | |
static PyTypeObject spamdict_type = { | |
PyVarObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type), 0) | |
"xxsubtype.spamdict", | |
sizeof(spamdictobject), | |
0, | |
0, /* tp_dealloc */ | |
0, /* tp_print */ | |
0, /* tp_getattr */ | |
0, /* tp_setattr */ | |
0, /* tp_compare */ | |
0, /* tp_repr */ | |
0, /* 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 | Py_TPFLAGS_BASETYPE, /* tp_flags */ | |
0, /* tp_doc */ | |
0, /* tp_traverse */ | |
0, /* tp_clear */ | |
0, /* tp_richcompare */ | |
0, /* tp_weaklistoffset */ | |
0, /* tp_iter */ | |
0, /* tp_iternext */ | |
spamdict_methods, /* tp_methods */ | |
spamdict_members, /* tp_members */ | |
0, /* tp_getset */ | |
DEFERRED_ADDRESS(&PyDict_Type), /* tp_base */ | |
0, /* tp_dict */ | |
0, /* tp_descr_get */ | |
0, /* tp_descr_set */ | |
0, /* tp_dictoffset */ | |
(initproc)spamdict_init, /* tp_init */ | |
0, /* tp_alloc */ | |
0, /* tp_new */ | |
}; | |
static PyObject * | |
spam_bench(PyObject *self, PyObject *args) | |
{ | |
PyObject *obj, *name, *res; | |
int n = 1000; | |
time_t t0, t1; | |
if (!PyArg_ParseTuple(args, "OS|i", &obj, &name, &n)) | |
return NULL; | |
t0 = clock(); | |
while (--n >= 0) { | |
res = PyObject_GetAttr(obj, name); | |
if (res == NULL) | |
return NULL; | |
Py_DECREF(res); | |
} | |
t1 = clock(); | |
return PyFloat_FromDouble((double)(t1-t0) / CLOCKS_PER_SEC); | |
} | |
static PyMethodDef xxsubtype_functions[] = { | |
{"bench", spam_bench, METH_VARARGS}, | |
{NULL, NULL} /* sentinel */ | |
}; | |
PyMODINIT_FUNC | |
initxxsubtype(void) | |
{ | |
PyObject *m; | |
/* Fill in deferred data addresses. This must be done before | |
PyType_Ready() is called. Note that PyType_Ready() automatically | |
initializes the ob.ob_type field to &PyType_Type if it's NULL, | |
so it's not necessary to fill in ob_type first. */ | |
spamdict_type.tp_base = &PyDict_Type; | |
if (PyType_Ready(&spamdict_type) < 0) | |
return; | |
spamlist_type.tp_base = &PyList_Type; | |
if (PyType_Ready(&spamlist_type) < 0) | |
return; | |
m = Py_InitModule3("xxsubtype", | |
xxsubtype_functions, | |
xxsubtype__doc__); | |
if (m == NULL) | |
return; | |
if (PyType_Ready(&spamlist_type) < 0) | |
return; | |
if (PyType_Ready(&spamdict_type) < 0) | |
return; | |
Py_INCREF(&spamlist_type); | |
if (PyModule_AddObject(m, "spamlist", | |
(PyObject *) &spamlist_type) < 0) | |
return; | |
Py_INCREF(&spamdict_type); | |
if (PyModule_AddObject(m, "spamdict", | |
(PyObject *) &spamdict_type) < 0) | |
return; | |
} |