1#include "Python.h"
2#include "structmember.h"
3
4PyDoc_STRVAR(xxsubtype__doc__,
5"xxsubtype is an example module showing how to subtype builtin types from C.\n"
6"test_descr.py in the standard test suite requires it in order to complete.\n"
7"If you don't care about the examples, and don't intend to run the Python\n"
8"test suite, you can recompile Python without Modules/xxsubtype.c.");
9
10/* We link this module statically for convenience.  If compiled as a shared
11   library instead, some compilers don't allow addresses of Python objects
12   defined in other libraries to be used in static initializers here.  The
13   DEFERRED_ADDRESS macro is used to tag the slots where such addresses
14   appear; the module init function must fill in the tagged slots at runtime.
15   The argument is for documentation -- the macro ignores it.
16*/
17#define DEFERRED_ADDRESS(ADDR) 0
18
19/* spamlist -- a list subtype */
20
21typedef struct {
22    PyListObject list;
23    int state;
24} spamlistobject;
25
26static PyObject *
27spamlist_getstate(spamlistobject *self, PyObject *args)
28{
29    if (!PyArg_ParseTuple(args, ":getstate"))
30        return NULL;
31    return PyInt_FromLong(self->state);
32}
33
34static PyObject *
35spamlist_setstate(spamlistobject *self, PyObject *args)
36{
37    int state;
38
39    if (!PyArg_ParseTuple(args, "i:setstate", &state))
40        return NULL;
41    self->state = state;
42    Py_INCREF(Py_None);
43    return Py_None;
44}
45
46static PyObject *
47spamlist_specialmeth(PyObject *self, PyObject *args, PyObject *kw)
48{
49    PyObject *result = PyTuple_New(3);
50
51    if (result != NULL) {
52        if (self == NULL)
53            self = Py_None;
54        if (kw == NULL)
55            kw = Py_None;
56        Py_INCREF(self);
57        PyTuple_SET_ITEM(result, 0, self);
58        Py_INCREF(args);
59        PyTuple_SET_ITEM(result, 1, args);
60        Py_INCREF(kw);
61        PyTuple_SET_ITEM(result, 2, kw);
62    }
63    return result;
64}
65
66static PyMethodDef spamlist_methods[] = {
67    {"getstate", (PyCFunction)spamlist_getstate, METH_VARARGS,
68        PyDoc_STR("getstate() -> state")},
69    {"setstate", (PyCFunction)spamlist_setstate, METH_VARARGS,
70        PyDoc_STR("setstate(state)")},
71    /* These entries differ only in the flags; they are used by the tests
72       in test.test_descr. */
73    {"classmeth", (PyCFunction)spamlist_specialmeth,
74        METH_VARARGS | METH_KEYWORDS | METH_CLASS,
75        PyDoc_STR("classmeth(*args, **kw)")},
76    {"staticmeth", (PyCFunction)spamlist_specialmeth,
77        METH_VARARGS | METH_KEYWORDS | METH_STATIC,
78        PyDoc_STR("staticmeth(*args, **kw)")},
79    {NULL,      NULL},
80};
81
82static int
83spamlist_init(spamlistobject *self, PyObject *args, PyObject *kwds)
84{
85    if (PyList_Type.tp_init((PyObject *)self, args, kwds) < 0)
86        return -1;
87    self->state = 0;
88    return 0;
89}
90
91static PyObject *
92spamlist_state_get(spamlistobject *self)
93{
94    return PyInt_FromLong(self->state);
95}
96
97static PyGetSetDef spamlist_getsets[] = {
98    {"state", (getter)spamlist_state_get, NULL,
99     PyDoc_STR("an int variable for demonstration purposes")},
100    {0}
101};
102
103static PyTypeObject spamlist_type = {
104    PyVarObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type), 0)
105    "xxsubtype.spamlist",
106    sizeof(spamlistobject),
107    0,
108    0,                                          /* tp_dealloc */
109    0,                                          /* tp_print */
110    0,                                          /* tp_getattr */
111    0,                                          /* tp_setattr */
112    0,                                          /* tp_compare */
113    0,                                          /* tp_repr */
114    0,                                          /* tp_as_number */
115    0,                                          /* tp_as_sequence */
116    0,                                          /* tp_as_mapping */
117    0,                                          /* tp_hash */
118    0,                                          /* tp_call */
119    0,                                          /* tp_str */
120    0,                                          /* tp_getattro */
121    0,                                          /* tp_setattro */
122    0,                                          /* tp_as_buffer */
123    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
124    0,                                          /* tp_doc */
125    0,                                          /* tp_traverse */
126    0,                                          /* tp_clear */
127    0,                                          /* tp_richcompare */
128    0,                                          /* tp_weaklistoffset */
129    0,                                          /* tp_iter */
130    0,                                          /* tp_iternext */
131    spamlist_methods,                           /* tp_methods */
132    0,                                          /* tp_members */
133    spamlist_getsets,                           /* tp_getset */
134    DEFERRED_ADDRESS(&PyList_Type),             /* tp_base */
135    0,                                          /* tp_dict */
136    0,                                          /* tp_descr_get */
137    0,                                          /* tp_descr_set */
138    0,                                          /* tp_dictoffset */
139    (initproc)spamlist_init,                    /* tp_init */
140    0,                                          /* tp_alloc */
141    0,                                          /* tp_new */
142};
143
144/* spamdict -- a dict subtype */
145
146typedef struct {
147    PyDictObject dict;
148    int state;
149} spamdictobject;
150
151static PyObject *
152spamdict_getstate(spamdictobject *self, PyObject *args)
153{
154    if (!PyArg_ParseTuple(args, ":getstate"))
155        return NULL;
156    return PyInt_FromLong(self->state);
157}
158
159static PyObject *
160spamdict_setstate(spamdictobject *self, PyObject *args)
161{
162    int state;
163
164    if (!PyArg_ParseTuple(args, "i:setstate", &state))
165        return NULL;
166    self->state = state;
167    Py_INCREF(Py_None);
168    return Py_None;
169}
170
171static PyMethodDef spamdict_methods[] = {
172    {"getstate", (PyCFunction)spamdict_getstate, METH_VARARGS,
173        PyDoc_STR("getstate() -> state")},
174    {"setstate", (PyCFunction)spamdict_setstate, METH_VARARGS,
175        PyDoc_STR("setstate(state)")},
176    {NULL,      NULL},
177};
178
179static int
180spamdict_init(spamdictobject *self, PyObject *args, PyObject *kwds)
181{
182    if (PyDict_Type.tp_init((PyObject *)self, args, kwds) < 0)
183        return -1;
184    self->state = 0;
185    return 0;
186}
187
188static PyMemberDef spamdict_members[] = {
189    {"state", T_INT, offsetof(spamdictobject, state), READONLY,
190     PyDoc_STR("an int variable for demonstration purposes")},
191    {0}
192};
193
194static PyTypeObject spamdict_type = {
195    PyVarObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type), 0)
196    "xxsubtype.spamdict",
197    sizeof(spamdictobject),
198    0,
199    0,                                          /* tp_dealloc */
200    0,                                          /* tp_print */
201    0,                                          /* tp_getattr */
202    0,                                          /* tp_setattr */
203    0,                                          /* tp_compare */
204    0,                                          /* tp_repr */
205    0,                                          /* tp_as_number */
206    0,                                          /* tp_as_sequence */
207    0,                                          /* tp_as_mapping */
208    0,                                          /* tp_hash */
209    0,                                          /* tp_call */
210    0,                                          /* tp_str */
211    0,                                          /* tp_getattro */
212    0,                                          /* tp_setattro */
213    0,                                          /* tp_as_buffer */
214    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
215    0,                                          /* tp_doc */
216    0,                                          /* tp_traverse */
217    0,                                          /* tp_clear */
218    0,                                          /* tp_richcompare */
219    0,                                          /* tp_weaklistoffset */
220    0,                                          /* tp_iter */
221    0,                                          /* tp_iternext */
222    spamdict_methods,                           /* tp_methods */
223    spamdict_members,                           /* tp_members */
224    0,                                          /* tp_getset */
225    DEFERRED_ADDRESS(&PyDict_Type),             /* tp_base */
226    0,                                          /* tp_dict */
227    0,                                          /* tp_descr_get */
228    0,                                          /* tp_descr_set */
229    0,                                          /* tp_dictoffset */
230    (initproc)spamdict_init,                    /* tp_init */
231    0,                                          /* tp_alloc */
232    0,                                          /* tp_new */
233};
234
235static PyObject *
236spam_bench(PyObject *self, PyObject *args)
237{
238    PyObject *obj, *name, *res;
239    int n = 1000;
240    time_t t0, t1;
241
242    if (!PyArg_ParseTuple(args, "OS|i", &obj, &name, &n))
243        return NULL;
244    t0 = clock();
245    while (--n >= 0) {
246        res = PyObject_GetAttr(obj, name);
247        if (res == NULL)
248            return NULL;
249        Py_DECREF(res);
250    }
251    t1 = clock();
252    return PyFloat_FromDouble((double)(t1-t0) / CLOCKS_PER_SEC);
253}
254
255static PyMethodDef xxsubtype_functions[] = {
256    {"bench",           spam_bench,     METH_VARARGS},
257    {NULL,              NULL}           /* sentinel */
258};
259
260PyMODINIT_FUNC
261initxxsubtype(void)
262{
263    PyObject *m;
264
265    /* Fill in deferred data addresses.  This must be done before
266       PyType_Ready() is called.  Note that PyType_Ready() automatically
267       initializes the ob.ob_type field to &PyType_Type if it's NULL,
268       so it's not necessary to fill in ob_type first. */
269    spamdict_type.tp_base = &PyDict_Type;
270    if (PyType_Ready(&spamdict_type) < 0)
271        return;
272
273    spamlist_type.tp_base = &PyList_Type;
274    if (PyType_Ready(&spamlist_type) < 0)
275        return;
276
277    m = Py_InitModule3("xxsubtype",
278                       xxsubtype_functions,
279                       xxsubtype__doc__);
280    if (m == NULL)
281        return;
282
283    if (PyType_Ready(&spamlist_type) < 0)
284        return;
285    if (PyType_Ready(&spamdict_type) < 0)
286        return;
287
288    Py_INCREF(&spamlist_type);
289    if (PyModule_AddObject(m, "spamlist",
290                           (PyObject *) &spamlist_type) < 0)
291        return;
292
293    Py_INCREF(&spamdict_type);
294    if (PyModule_AddObject(m, "spamdict",
295                           (PyObject *) &spamdict_type) < 0)
296        return;
297}
298