blob: 9bd9a5407d43c4c1049f87c4e7377a704b06efbe [file] [log] [blame]
Martin v. Löwis4d0d4712010-12-03 20:14:31 +00001
2/* Use this file as a template to start implementing a module that
3 also declares object types. All occurrences of 'Xxo' should be changed
4 to something reasonable for your objects. After that, all other
5 occurrences of 'xx' should be changed to something reasonable for your
Petr Viktorinc168b502020-12-08 17:36:53 +01006 module. If your module is named foo your source file should be named
7 foo.c or foomodule.c.
Martin v. Löwis4d0d4712010-12-03 20:14:31 +00008
9 You will probably want to delete all references to 'x_attr' and add
10 your own types of attributes instead. Maybe you want to name your
11 local variables other than 'self'. If your object type is needed in
12 other files, you'll have to create a file "foobarobject.h"; see
Petr Viktorinc168b502020-12-08 17:36:53 +010013 floatobject.h for an example.
Martin v. Löwis4d0d4712010-12-03 20:14:31 +000014
Petr Viktorinc168b502020-12-08 17:36:53 +010015 This module roughly corresponds to::
16
17 class Xxo:
18 """A class that explicitly stores attributes in an internal dict"""
19
20 def __init__(self):
21 # In the C class, "_x_attr" is not accessible from Python code
22 self._x_attr = {}
23
24 def __getattr__(self, name):
25 return self._x_attr[name]
26
27 def __setattr__(self, name, value):
28 self._x_attr[name] = value
29
30 def __delattr__(self, name):
31 del self._x_attr[name]
32
33 def demo(o, /):
34 if isinstance(o, str):
35 return o
36 elif isinstance(o, Xxo):
37 return o
38 else:
39 raise Error('argument must be str or Xxo')
40
41 class Error(Exception):
42 """Exception raised by the xxlimited module"""
43
44 def foo(i: int, j: int, /):
45 """Return the sum of i and j."""
46 # Unlike this pseudocode, the C function will *only* work with
47 # integers and perform C long int arithmetic
48 return i + j
49
50 def new():
51 return Xxo()
52
53 def Str(str):
54 # A trivial subclass of a built-in type
55 pass
56 */
Martin v. Löwis4d0d4712010-12-03 20:14:31 +000057
Victor Stinner240bcf82021-04-02 16:48:11 +020058#define Py_LIMITED_API 0x030a0000
59
Martin v. Löwis4d0d4712010-12-03 20:14:31 +000060#include "Python.h"
61
Petr Viktorinc168b502020-12-08 17:36:53 +010062// Module state
63typedef struct {
64 PyObject *Xxo_Type; // Xxo class
65 PyObject *Error_Type; // Error class
66} xx_state;
Martin v. Löwis4d0d4712010-12-03 20:14:31 +000067
Petr Viktorinc168b502020-12-08 17:36:53 +010068
69/* Xxo objects */
70
71// Instance state
Martin v. Löwis4d0d4712010-12-03 20:14:31 +000072typedef struct {
73 PyObject_HEAD
74 PyObject *x_attr; /* Attributes dictionary */
75} XxoObject;
76
Petr Viktorinc168b502020-12-08 17:36:53 +010077// XXX: no good way to do this yet
78// #define XxoObject_Check(v) Py_IS_TYPE(v, Xxo_Type)
Martin v. Löwis4d0d4712010-12-03 20:14:31 +000079
80static XxoObject *
Petr Viktorinc168b502020-12-08 17:36:53 +010081newXxoObject(PyObject *module)
Martin v. Löwis4d0d4712010-12-03 20:14:31 +000082{
Petr Viktorinc168b502020-12-08 17:36:53 +010083 xx_state *state = PyModule_GetState(module);
84 if (state == NULL) {
Martin v. Löwis4d0d4712010-12-03 20:14:31 +000085 return NULL;
Petr Viktorinc168b502020-12-08 17:36:53 +010086 }
87 XxoObject *self;
88 self = PyObject_GC_New(XxoObject, (PyTypeObject*)state->Xxo_Type);
89 if (self == NULL) {
90 return NULL;
91 }
Martin v. Löwis4d0d4712010-12-03 20:14:31 +000092 self->x_attr = NULL;
93 return self;
94}
95
Petr Viktorinc168b502020-12-08 17:36:53 +010096/* Xxo finalization */
Martin v. Löwis4d0d4712010-12-03 20:14:31 +000097
Nick Coghlan53f95022015-06-04 21:52:57 +100098static int
99Xxo_traverse(XxoObject *self, visitproc visit, void *arg)
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000100{
Petr Viktorinc168b502020-12-08 17:36:53 +0100101 // Visit the type
Pablo Galindo1cf15af2020-05-27 10:03:38 +0100102 Py_VISIT(Py_TYPE(self));
Petr Viktorinc168b502020-12-08 17:36:53 +0100103
104 // Visit the attribute dict
Nick Coghlan53f95022015-06-04 21:52:57 +1000105 Py_VISIT(self->x_attr);
106 return 0;
107}
108
Miss Islington (bot)f097d232021-05-31 04:23:13 -0700109static int
110Xxo_clear(XxoObject *self)
111{
112 Py_CLEAR(self->x_attr);
113 return 0;
114}
115
Serhiy Storchaka19de8b32018-05-26 10:51:58 +0300116static void
Nick Coghlan53f95022015-06-04 21:52:57 +1000117Xxo_finalize(XxoObject *self)
118{
119 Py_CLEAR(self->x_attr);
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000120}
121
Petr Viktorinc168b502020-12-08 17:36:53 +0100122static void
123Xxo_dealloc(XxoObject *self)
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000124{
Petr Viktorinc168b502020-12-08 17:36:53 +0100125 Xxo_finalize(self);
126 PyTypeObject *tp = Py_TYPE(self);
127 freefunc free = PyType_GetSlot(tp, Py_tp_free);
128 free(self);
129 Py_DECREF(tp);
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000130}
131
Petr Viktorinc168b502020-12-08 17:36:53 +0100132
133/* Xxo attribute handling */
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000134
135static PyObject *
136Xxo_getattro(XxoObject *self, PyObject *name)
137{
138 if (self->x_attr != NULL) {
Serhiy Storchakaa24107b2019-02-25 17:59:46 +0200139 PyObject *v = PyDict_GetItemWithError(self->x_attr, name);
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000140 if (v != NULL) {
141 Py_INCREF(v);
142 return v;
143 }
Serhiy Storchakaa24107b2019-02-25 17:59:46 +0200144 else if (PyErr_Occurred()) {
145 return NULL;
146 }
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000147 }
148 return PyObject_GenericGetAttr((PyObject *)self, name);
149}
150
151static int
Petr Viktorinc168b502020-12-08 17:36:53 +0100152Xxo_setattro(XxoObject *self, PyObject *name, PyObject *v)
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000153{
154 if (self->x_attr == NULL) {
Petr Viktorinc168b502020-12-08 17:36:53 +0100155 // prepare the attribute dict
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000156 self->x_attr = PyDict_New();
Petr Viktorinc168b502020-12-08 17:36:53 +0100157 if (self->x_attr == NULL) {
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000158 return -1;
Petr Viktorinc168b502020-12-08 17:36:53 +0100159 }
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000160 }
161 if (v == NULL) {
Petr Viktorinc168b502020-12-08 17:36:53 +0100162 // delete an attribute
163 int rv = PyDict_DelItem(self->x_attr, name);
164 if (rv < 0 && PyErr_ExceptionMatches(PyExc_KeyError)) {
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000165 PyErr_SetString(PyExc_AttributeError,
166 "delete non-existing Xxo attribute");
Petr Viktorinc168b502020-12-08 17:36:53 +0100167 return -1;
168 }
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000169 return rv;
170 }
Petr Viktorinc168b502020-12-08 17:36:53 +0100171 else {
172 // set an attribute
173 return PyDict_SetItem(self->x_attr, name, v);
174 }
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000175}
176
Petr Viktorinc168b502020-12-08 17:36:53 +0100177/* Xxo methods */
178
179static PyObject *
180Xxo_demo(XxoObject *self, PyTypeObject *defining_class,
181 PyObject **args, Py_ssize_t nargs, PyObject *kwnames)
182{
183 if (kwnames != NULL && PyObject_Length(kwnames)) {
184 PyErr_SetString(PyExc_TypeError, "demo() takes no keyword arguments");
185 return NULL;
186 }
187 if (nargs != 1) {
188 PyErr_SetString(PyExc_TypeError, "demo() takes exactly 1 argument");
189 return NULL;
190 }
191
192 PyObject *o = args[0];
193
194 /* Test if the argument is "str" */
195 if (PyUnicode_Check(o)) {
196 Py_INCREF(o);
197 return o;
198 }
199
200 /* test if the argument is of the Xxo class */
201 if (PyObject_TypeCheck(o, defining_class)) {
202 Py_INCREF(o);
203 return o;
204 }
205
206 Py_INCREF(Py_None);
207 return Py_None;
208}
209
210static PyMethodDef Xxo_methods[] = {
211 {"demo", (PyCFunction)(void(*)(void))Xxo_demo,
212 METH_METHOD | METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("demo(o) -> o")},
213 {NULL, NULL} /* sentinel */
214};
215
216/* Xxo type definition */
217
218PyDoc_STRVAR(Xxo_doc,
219 "A class that explicitly stores attributes in an internal dict");
220
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000221static PyType_Slot Xxo_Type_slots[] = {
Petr Viktorinc168b502020-12-08 17:36:53 +0100222 {Py_tp_doc, (char *)Xxo_doc},
Nick Coghlan53f95022015-06-04 21:52:57 +1000223 {Py_tp_traverse, Xxo_traverse},
Miss Islington (bot)f097d232021-05-31 04:23:13 -0700224 {Py_tp_clear, Xxo_clear},
Nick Coghlan53f95022015-06-04 21:52:57 +1000225 {Py_tp_finalize, Xxo_finalize},
Petr Viktorinc168b502020-12-08 17:36:53 +0100226 {Py_tp_dealloc, Xxo_dealloc},
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000227 {Py_tp_getattro, Xxo_getattro},
Petr Viktorinc168b502020-12-08 17:36:53 +0100228 {Py_tp_setattro, Xxo_setattro},
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000229 {Py_tp_methods, Xxo_methods},
Petr Viktorinc168b502020-12-08 17:36:53 +0100230 {0, 0}, /* sentinel */
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000231};
232
233static PyType_Spec Xxo_Type_spec = {
Petr Viktorinc168b502020-12-08 17:36:53 +0100234 .name = "xxlimited.Xxo",
235 .basicsize = sizeof(XxoObject),
236 .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
237 .slots = Xxo_Type_slots,
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000238};
239
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000240
Petr Viktorinc168b502020-12-08 17:36:53 +0100241/* Str type definition*/
242
243static PyType_Slot Str_Type_slots[] = {
244 {0, 0}, /* sentinel */
245};
246
247static PyType_Spec Str_Type_spec = {
248 .name = "xxlimited.Str",
249 .basicsize = 0,
250 .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
251 .slots = Str_Type_slots,
252};
253
254
255/* Function of two integers returning integer (with C "long int" arithmetic) */
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000256
257PyDoc_STRVAR(xx_foo_doc,
258"foo(i,j)\n\
259\n\
260Return the sum of i and j.");
261
262static PyObject *
Petr Viktorinc168b502020-12-08 17:36:53 +0100263xx_foo(PyObject *module, PyObject *args)
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000264{
265 long i, j;
266 long res;
267 if (!PyArg_ParseTuple(args, "ll:foo", &i, &j))
268 return NULL;
269 res = i+j; /* XXX Do something here */
270 return PyLong_FromLong(res);
271}
272
273
274/* Function of no arguments returning new Xxo object */
275
276static PyObject *
Petr Viktorinc168b502020-12-08 17:36:53 +0100277xx_new(PyObject *module, PyObject *Py_UNUSED(unused))
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000278{
279 XxoObject *rv;
280
Petr Viktorinc168b502020-12-08 17:36:53 +0100281 rv = newXxoObject(module);
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000282 if (rv == NULL)
283 return NULL;
284 return (PyObject *)rv;
285}
286
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000287
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000288
289/* List of functions defined in the module */
290
291static PyMethodDef xx_methods[] = {
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000292 {"foo", xx_foo, METH_VARARGS,
293 xx_foo_doc},
Petr Viktorinc168b502020-12-08 17:36:53 +0100294 {"new", xx_new, METH_NOARGS,
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000295 PyDoc_STR("new() -> new Xx object")},
296 {NULL, NULL} /* sentinel */
297};
298
Petr Viktorinc168b502020-12-08 17:36:53 +0100299
300/* The module itself */
301
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000302PyDoc_STRVAR(module_doc,
303"This is a template module just for instruction.");
304
Nick Coghland5cacbb2015-05-23 22:24:10 +1000305static int
306xx_modexec(PyObject *m)
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000307{
Petr Viktorinc168b502020-12-08 17:36:53 +0100308 xx_state *state = PyModule_GetState(m);
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000309
Petr Viktorinc168b502020-12-08 17:36:53 +0100310 state->Error_Type = PyErr_NewException("xxlimited.Error", NULL, NULL);
311 if (state->Error_Type == NULL) {
312 return -1;
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000313 }
Petr Viktorinc168b502020-12-08 17:36:53 +0100314 if (PyModule_AddType(m, (PyTypeObject*)state->Error_Type) < 0) {
315 return -1;
316 }
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000317
Petr Viktorinc168b502020-12-08 17:36:53 +0100318 state->Xxo_Type = PyType_FromModuleAndSpec(m, &Xxo_Type_spec, NULL);
319 if (state->Xxo_Type == NULL) {
320 return -1;
321 }
322 if (PyModule_AddType(m, (PyTypeObject*)state->Xxo_Type) < 0) {
323 return -1;
324 }
Nick Coghlan53f95022015-06-04 21:52:57 +1000325
Petr Viktorinc168b502020-12-08 17:36:53 +0100326 // Add the Str type. It is not needed from C code, so it is only
327 // added to the module dict.
328 // It does not inherit from "object" (PyObject_Type), but from "str"
329 // (PyUnincode_Type).
330 PyObject *Str_Type = PyType_FromModuleAndSpec(
331 m, &Str_Type_spec, (PyObject *)&PyUnicode_Type);
332 if (Str_Type == NULL) {
333 return -1;
334 }
335 if (PyModule_AddType(m, (PyTypeObject*)Str_Type) < 0) {
336 return -1;
337 }
338 Py_DECREF(Str_Type);
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000339
Nick Coghland5cacbb2015-05-23 22:24:10 +1000340 return 0;
Nick Coghland5cacbb2015-05-23 22:24:10 +1000341}
342
Nick Coghland5cacbb2015-05-23 22:24:10 +1000343static PyModuleDef_Slot xx_slots[] = {
344 {Py_mod_exec, xx_modexec},
345 {0, NULL}
346};
347
Petr Viktorinc168b502020-12-08 17:36:53 +0100348static int
349xx_traverse(PyObject *module, visitproc visit, void *arg)
350{
351 xx_state *state = PyModule_GetState(module);
352 Py_VISIT(state->Xxo_Type);
353 Py_VISIT(state->Error_Type);
354 return 0;
355}
356
357static int
358xx_clear(PyObject *module)
359{
360 xx_state *state = PyModule_GetState(module);
361 Py_CLEAR(state->Xxo_Type);
362 Py_CLEAR(state->Error_Type);
363 return 0;
364}
365
Nick Coghland5cacbb2015-05-23 22:24:10 +1000366static struct PyModuleDef xxmodule = {
367 PyModuleDef_HEAD_INIT,
Petr Viktorinc168b502020-12-08 17:36:53 +0100368 .m_name = "xxlimited",
369 .m_doc = module_doc,
370 .m_size = sizeof(xx_state),
371 .m_methods = xx_methods,
372 .m_slots = xx_slots,
373 .m_traverse = xx_traverse,
374 .m_clear = xx_clear,
375 /* m_free is not necessary here: xx_clear clears all references,
376 * and the module state is deallocated along with the module.
377 */
Nick Coghland5cacbb2015-05-23 22:24:10 +1000378};
379
Petr Viktorinc168b502020-12-08 17:36:53 +0100380
Nick Coghland5cacbb2015-05-23 22:24:10 +1000381/* Export function for the module (*must* be called PyInit_xx) */
382
383PyMODINIT_FUNC
384PyInit_xxlimited(void)
385{
386 return PyModuleDef_Init(&xxmodule);
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000387}