blob: c3d98d34eb5915f5bbe7f4e8019b25c8ff8cb71e [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
Serhiy Storchaka19de8b32018-05-26 10:51:58 +0300109static void
Nick Coghlan53f95022015-06-04 21:52:57 +1000110Xxo_finalize(XxoObject *self)
111{
112 Py_CLEAR(self->x_attr);
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000113}
114
Petr Viktorinc168b502020-12-08 17:36:53 +0100115static void
116Xxo_dealloc(XxoObject *self)
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000117{
Petr Viktorinc168b502020-12-08 17:36:53 +0100118 Xxo_finalize(self);
119 PyTypeObject *tp = Py_TYPE(self);
120 freefunc free = PyType_GetSlot(tp, Py_tp_free);
121 free(self);
122 Py_DECREF(tp);
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000123}
124
Petr Viktorinc168b502020-12-08 17:36:53 +0100125
126/* Xxo attribute handling */
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000127
128static PyObject *
129Xxo_getattro(XxoObject *self, PyObject *name)
130{
131 if (self->x_attr != NULL) {
Serhiy Storchakaa24107b2019-02-25 17:59:46 +0200132 PyObject *v = PyDict_GetItemWithError(self->x_attr, name);
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000133 if (v != NULL) {
134 Py_INCREF(v);
135 return v;
136 }
Serhiy Storchakaa24107b2019-02-25 17:59:46 +0200137 else if (PyErr_Occurred()) {
138 return NULL;
139 }
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000140 }
141 return PyObject_GenericGetAttr((PyObject *)self, name);
142}
143
144static int
Petr Viktorinc168b502020-12-08 17:36:53 +0100145Xxo_setattro(XxoObject *self, PyObject *name, PyObject *v)
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000146{
147 if (self->x_attr == NULL) {
Petr Viktorinc168b502020-12-08 17:36:53 +0100148 // prepare the attribute dict
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000149 self->x_attr = PyDict_New();
Petr Viktorinc168b502020-12-08 17:36:53 +0100150 if (self->x_attr == NULL) {
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000151 return -1;
Petr Viktorinc168b502020-12-08 17:36:53 +0100152 }
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000153 }
154 if (v == NULL) {
Petr Viktorinc168b502020-12-08 17:36:53 +0100155 // delete an attribute
156 int rv = PyDict_DelItem(self->x_attr, name);
157 if (rv < 0 && PyErr_ExceptionMatches(PyExc_KeyError)) {
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000158 PyErr_SetString(PyExc_AttributeError,
159 "delete non-existing Xxo attribute");
Petr Viktorinc168b502020-12-08 17:36:53 +0100160 return -1;
161 }
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000162 return rv;
163 }
Petr Viktorinc168b502020-12-08 17:36:53 +0100164 else {
165 // set an attribute
166 return PyDict_SetItem(self->x_attr, name, v);
167 }
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000168}
169
Petr Viktorinc168b502020-12-08 17:36:53 +0100170/* Xxo methods */
171
172static PyObject *
173Xxo_demo(XxoObject *self, PyTypeObject *defining_class,
174 PyObject **args, Py_ssize_t nargs, PyObject *kwnames)
175{
176 if (kwnames != NULL && PyObject_Length(kwnames)) {
177 PyErr_SetString(PyExc_TypeError, "demo() takes no keyword arguments");
178 return NULL;
179 }
180 if (nargs != 1) {
181 PyErr_SetString(PyExc_TypeError, "demo() takes exactly 1 argument");
182 return NULL;
183 }
184
185 PyObject *o = args[0];
186
187 /* Test if the argument is "str" */
188 if (PyUnicode_Check(o)) {
189 Py_INCREF(o);
190 return o;
191 }
192
193 /* test if the argument is of the Xxo class */
194 if (PyObject_TypeCheck(o, defining_class)) {
195 Py_INCREF(o);
196 return o;
197 }
198
199 Py_INCREF(Py_None);
200 return Py_None;
201}
202
203static PyMethodDef Xxo_methods[] = {
204 {"demo", (PyCFunction)(void(*)(void))Xxo_demo,
205 METH_METHOD | METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("demo(o) -> o")},
206 {NULL, NULL} /* sentinel */
207};
208
209/* Xxo type definition */
210
211PyDoc_STRVAR(Xxo_doc,
212 "A class that explicitly stores attributes in an internal dict");
213
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000214static PyType_Slot Xxo_Type_slots[] = {
Petr Viktorinc168b502020-12-08 17:36:53 +0100215 {Py_tp_doc, (char *)Xxo_doc},
Nick Coghlan53f95022015-06-04 21:52:57 +1000216 {Py_tp_traverse, Xxo_traverse},
217 {Py_tp_finalize, Xxo_finalize},
Petr Viktorinc168b502020-12-08 17:36:53 +0100218 {Py_tp_dealloc, Xxo_dealloc},
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000219 {Py_tp_getattro, Xxo_getattro},
Petr Viktorinc168b502020-12-08 17:36:53 +0100220 {Py_tp_setattro, Xxo_setattro},
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000221 {Py_tp_methods, Xxo_methods},
Petr Viktorinc168b502020-12-08 17:36:53 +0100222 {0, 0}, /* sentinel */
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000223};
224
225static PyType_Spec Xxo_Type_spec = {
Petr Viktorinc168b502020-12-08 17:36:53 +0100226 .name = "xxlimited.Xxo",
227 .basicsize = sizeof(XxoObject),
228 .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
229 .slots = Xxo_Type_slots,
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000230};
231
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000232
Petr Viktorinc168b502020-12-08 17:36:53 +0100233/* Str type definition*/
234
235static PyType_Slot Str_Type_slots[] = {
236 {0, 0}, /* sentinel */
237};
238
239static PyType_Spec Str_Type_spec = {
240 .name = "xxlimited.Str",
241 .basicsize = 0,
242 .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
243 .slots = Str_Type_slots,
244};
245
246
247/* Function of two integers returning integer (with C "long int" arithmetic) */
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000248
249PyDoc_STRVAR(xx_foo_doc,
250"foo(i,j)\n\
251\n\
252Return the sum of i and j.");
253
254static PyObject *
Petr Viktorinc168b502020-12-08 17:36:53 +0100255xx_foo(PyObject *module, PyObject *args)
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000256{
257 long i, j;
258 long res;
259 if (!PyArg_ParseTuple(args, "ll:foo", &i, &j))
260 return NULL;
261 res = i+j; /* XXX Do something here */
262 return PyLong_FromLong(res);
263}
264
265
266/* Function of no arguments returning new Xxo object */
267
268static PyObject *
Petr Viktorinc168b502020-12-08 17:36:53 +0100269xx_new(PyObject *module, PyObject *Py_UNUSED(unused))
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000270{
271 XxoObject *rv;
272
Petr Viktorinc168b502020-12-08 17:36:53 +0100273 rv = newXxoObject(module);
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000274 if (rv == NULL)
275 return NULL;
276 return (PyObject *)rv;
277}
278
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000279
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000280
281/* List of functions defined in the module */
282
283static PyMethodDef xx_methods[] = {
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000284 {"foo", xx_foo, METH_VARARGS,
285 xx_foo_doc},
Petr Viktorinc168b502020-12-08 17:36:53 +0100286 {"new", xx_new, METH_NOARGS,
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000287 PyDoc_STR("new() -> new Xx object")},
288 {NULL, NULL} /* sentinel */
289};
290
Petr Viktorinc168b502020-12-08 17:36:53 +0100291
292/* The module itself */
293
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000294PyDoc_STRVAR(module_doc,
295"This is a template module just for instruction.");
296
Nick Coghland5cacbb2015-05-23 22:24:10 +1000297static int
298xx_modexec(PyObject *m)
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000299{
Petr Viktorinc168b502020-12-08 17:36:53 +0100300 xx_state *state = PyModule_GetState(m);
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000301
Petr Viktorinc168b502020-12-08 17:36:53 +0100302 state->Error_Type = PyErr_NewException("xxlimited.Error", NULL, NULL);
303 if (state->Error_Type == NULL) {
304 return -1;
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000305 }
Petr Viktorinc168b502020-12-08 17:36:53 +0100306 if (PyModule_AddType(m, (PyTypeObject*)state->Error_Type) < 0) {
307 return -1;
308 }
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000309
Petr Viktorinc168b502020-12-08 17:36:53 +0100310 state->Xxo_Type = PyType_FromModuleAndSpec(m, &Xxo_Type_spec, NULL);
311 if (state->Xxo_Type == NULL) {
312 return -1;
313 }
314 if (PyModule_AddType(m, (PyTypeObject*)state->Xxo_Type) < 0) {
315 return -1;
316 }
Nick Coghlan53f95022015-06-04 21:52:57 +1000317
Petr Viktorinc168b502020-12-08 17:36:53 +0100318 // Add the Str type. It is not needed from C code, so it is only
319 // added to the module dict.
320 // It does not inherit from "object" (PyObject_Type), but from "str"
321 // (PyUnincode_Type).
322 PyObject *Str_Type = PyType_FromModuleAndSpec(
323 m, &Str_Type_spec, (PyObject *)&PyUnicode_Type);
324 if (Str_Type == NULL) {
325 return -1;
326 }
327 if (PyModule_AddType(m, (PyTypeObject*)Str_Type) < 0) {
328 return -1;
329 }
330 Py_DECREF(Str_Type);
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000331
Nick Coghland5cacbb2015-05-23 22:24:10 +1000332 return 0;
Nick Coghland5cacbb2015-05-23 22:24:10 +1000333}
334
Nick Coghland5cacbb2015-05-23 22:24:10 +1000335static PyModuleDef_Slot xx_slots[] = {
336 {Py_mod_exec, xx_modexec},
337 {0, NULL}
338};
339
Petr Viktorinc168b502020-12-08 17:36:53 +0100340static int
341xx_traverse(PyObject *module, visitproc visit, void *arg)
342{
343 xx_state *state = PyModule_GetState(module);
344 Py_VISIT(state->Xxo_Type);
345 Py_VISIT(state->Error_Type);
346 return 0;
347}
348
349static int
350xx_clear(PyObject *module)
351{
352 xx_state *state = PyModule_GetState(module);
353 Py_CLEAR(state->Xxo_Type);
354 Py_CLEAR(state->Error_Type);
355 return 0;
356}
357
Nick Coghland5cacbb2015-05-23 22:24:10 +1000358static struct PyModuleDef xxmodule = {
359 PyModuleDef_HEAD_INIT,
Petr Viktorinc168b502020-12-08 17:36:53 +0100360 .m_name = "xxlimited",
361 .m_doc = module_doc,
362 .m_size = sizeof(xx_state),
363 .m_methods = xx_methods,
364 .m_slots = xx_slots,
365 .m_traverse = xx_traverse,
366 .m_clear = xx_clear,
367 /* m_free is not necessary here: xx_clear clears all references,
368 * and the module state is deallocated along with the module.
369 */
Nick Coghland5cacbb2015-05-23 22:24:10 +1000370};
371
Petr Viktorinc168b502020-12-08 17:36:53 +0100372
Nick Coghland5cacbb2015-05-23 22:24:10 +1000373/* Export function for the module (*must* be called PyInit_xx) */
374
375PyMODINIT_FUNC
376PyInit_xxlimited(void)
377{
378 return PyModuleDef_Init(&xxmodule);
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000379}