• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include "Python.h"
2 #include "structmember.h"
3 
4 PyDoc_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 
21 typedef struct {
22     PyListObject list;
23     int state;
24 } spamlistobject;
25 
26 static PyObject *
spamlist_getstate(spamlistobject * self,PyObject * args)27 spamlist_getstate(spamlistobject *self, PyObject *args)
28 {
29     if (!PyArg_ParseTuple(args, ":getstate"))
30         return NULL;
31     return PyInt_FromLong(self->state);
32 }
33 
34 static PyObject *
spamlist_setstate(spamlistobject * self,PyObject * args)35 spamlist_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 
46 static PyObject *
spamlist_specialmeth(PyObject * self,PyObject * args,PyObject * kw)47 spamlist_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 
66 static 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 
82 static int
spamlist_init(spamlistobject * self,PyObject * args,PyObject * kwds)83 spamlist_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 
91 static PyObject *
spamlist_state_get(spamlistobject * self)92 spamlist_state_get(spamlistobject *self)
93 {
94     return PyInt_FromLong(self->state);
95 }
96 
97 static PyGetSetDef spamlist_getsets[] = {
98     {"state", (getter)spamlist_state_get, NULL,
99      PyDoc_STR("an int variable for demonstration purposes")},
100     {0}
101 };
102 
103 static 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 
146 typedef struct {
147     PyDictObject dict;
148     int state;
149 } spamdictobject;
150 
151 static PyObject *
spamdict_getstate(spamdictobject * self,PyObject * args)152 spamdict_getstate(spamdictobject *self, PyObject *args)
153 {
154     if (!PyArg_ParseTuple(args, ":getstate"))
155         return NULL;
156     return PyInt_FromLong(self->state);
157 }
158 
159 static PyObject *
spamdict_setstate(spamdictobject * self,PyObject * args)160 spamdict_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 
171 static 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 
179 static int
spamdict_init(spamdictobject * self,PyObject * args,PyObject * kwds)180 spamdict_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 
188 static PyMemberDef spamdict_members[] = {
189     {"state", T_INT, offsetof(spamdictobject, state), READONLY,
190      PyDoc_STR("an int variable for demonstration purposes")},
191     {0}
192 };
193 
194 static 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 
235 static PyObject *
spam_bench(PyObject * self,PyObject * args)236 spam_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 
255 static PyMethodDef xxsubtype_functions[] = {
256     {"bench",           spam_bench,     METH_VARARGS},
257     {NULL,              NULL}           /* sentinel */
258 };
259 
260 PyMODINIT_FUNC
initxxsubtype(void)261 initxxsubtype(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