blob: 883c8a9b5e1833c851ca29b813580caff694b1db [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
58#include "Python.h"
59
Petr Viktorinc168b502020-12-08 17:36:53 +010060// Module state
61typedef struct {
62 PyObject *Xxo_Type; // Xxo class
63 PyObject *Error_Type; // Error class
64} xx_state;
Martin v. Löwis4d0d4712010-12-03 20:14:31 +000065
Petr Viktorinc168b502020-12-08 17:36:53 +010066
67/* Xxo objects */
68
69// Instance state
Martin v. Löwis4d0d4712010-12-03 20:14:31 +000070typedef struct {
71 PyObject_HEAD
72 PyObject *x_attr; /* Attributes dictionary */
73} XxoObject;
74
Petr Viktorinc168b502020-12-08 17:36:53 +010075// XXX: no good way to do this yet
76// #define XxoObject_Check(v) Py_IS_TYPE(v, Xxo_Type)
Martin v. Löwis4d0d4712010-12-03 20:14:31 +000077
78static XxoObject *
Petr Viktorinc168b502020-12-08 17:36:53 +010079newXxoObject(PyObject *module)
Martin v. Löwis4d0d4712010-12-03 20:14:31 +000080{
Petr Viktorinc168b502020-12-08 17:36:53 +010081 xx_state *state = PyModule_GetState(module);
82 if (state == NULL) {
Martin v. Löwis4d0d4712010-12-03 20:14:31 +000083 return NULL;
Petr Viktorinc168b502020-12-08 17:36:53 +010084 }
85 XxoObject *self;
86 self = PyObject_GC_New(XxoObject, (PyTypeObject*)state->Xxo_Type);
87 if (self == NULL) {
88 return NULL;
89 }
Martin v. Löwis4d0d4712010-12-03 20:14:31 +000090 self->x_attr = NULL;
91 return self;
92}
93
Petr Viktorinc168b502020-12-08 17:36:53 +010094/* Xxo finalization */
Martin v. Löwis4d0d4712010-12-03 20:14:31 +000095
Nick Coghlan53f95022015-06-04 21:52:57 +100096static int
97Xxo_traverse(XxoObject *self, visitproc visit, void *arg)
Martin v. Löwis4d0d4712010-12-03 20:14:31 +000098{
Petr Viktorinc168b502020-12-08 17:36:53 +010099 // Visit the type
Pablo Galindo1cf15af2020-05-27 10:03:38 +0100100 Py_VISIT(Py_TYPE(self));
Petr Viktorinc168b502020-12-08 17:36:53 +0100101
102 // Visit the attribute dict
Nick Coghlan53f95022015-06-04 21:52:57 +1000103 Py_VISIT(self->x_attr);
104 return 0;
105}
106
Serhiy Storchaka19de8b32018-05-26 10:51:58 +0300107static void
Nick Coghlan53f95022015-06-04 21:52:57 +1000108Xxo_finalize(XxoObject *self)
109{
110 Py_CLEAR(self->x_attr);
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000111}
112
Petr Viktorinc168b502020-12-08 17:36:53 +0100113static void
114Xxo_dealloc(XxoObject *self)
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000115{
Petr Viktorinc168b502020-12-08 17:36:53 +0100116 Xxo_finalize(self);
117 PyTypeObject *tp = Py_TYPE(self);
118 freefunc free = PyType_GetSlot(tp, Py_tp_free);
119 free(self);
120 Py_DECREF(tp);
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000121}
122
Petr Viktorinc168b502020-12-08 17:36:53 +0100123
124/* Xxo attribute handling */
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000125
126static PyObject *
127Xxo_getattro(XxoObject *self, PyObject *name)
128{
129 if (self->x_attr != NULL) {
Serhiy Storchakaa24107b2019-02-25 17:59:46 +0200130 PyObject *v = PyDict_GetItemWithError(self->x_attr, name);
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000131 if (v != NULL) {
132 Py_INCREF(v);
133 return v;
134 }
Serhiy Storchakaa24107b2019-02-25 17:59:46 +0200135 else if (PyErr_Occurred()) {
136 return NULL;
137 }
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000138 }
139 return PyObject_GenericGetAttr((PyObject *)self, name);
140}
141
142static int
Petr Viktorinc168b502020-12-08 17:36:53 +0100143Xxo_setattro(XxoObject *self, PyObject *name, PyObject *v)
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000144{
145 if (self->x_attr == NULL) {
Petr Viktorinc168b502020-12-08 17:36:53 +0100146 // prepare the attribute dict
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000147 self->x_attr = PyDict_New();
Petr Viktorinc168b502020-12-08 17:36:53 +0100148 if (self->x_attr == NULL) {
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000149 return -1;
Petr Viktorinc168b502020-12-08 17:36:53 +0100150 }
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000151 }
152 if (v == NULL) {
Petr Viktorinc168b502020-12-08 17:36:53 +0100153 // delete an attribute
154 int rv = PyDict_DelItem(self->x_attr, name);
155 if (rv < 0 && PyErr_ExceptionMatches(PyExc_KeyError)) {
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000156 PyErr_SetString(PyExc_AttributeError,
157 "delete non-existing Xxo attribute");
Petr Viktorinc168b502020-12-08 17:36:53 +0100158 return -1;
159 }
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000160 return rv;
161 }
Petr Viktorinc168b502020-12-08 17:36:53 +0100162 else {
163 // set an attribute
164 return PyDict_SetItem(self->x_attr, name, v);
165 }
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000166}
167
Petr Viktorinc168b502020-12-08 17:36:53 +0100168/* Xxo methods */
169
170static PyObject *
171Xxo_demo(XxoObject *self, PyTypeObject *defining_class,
172 PyObject **args, Py_ssize_t nargs, PyObject *kwnames)
173{
174 if (kwnames != NULL && PyObject_Length(kwnames)) {
175 PyErr_SetString(PyExc_TypeError, "demo() takes no keyword arguments");
176 return NULL;
177 }
178 if (nargs != 1) {
179 PyErr_SetString(PyExc_TypeError, "demo() takes exactly 1 argument");
180 return NULL;
181 }
182
183 PyObject *o = args[0];
184
185 /* Test if the argument is "str" */
186 if (PyUnicode_Check(o)) {
187 Py_INCREF(o);
188 return o;
189 }
190
191 /* test if the argument is of the Xxo class */
192 if (PyObject_TypeCheck(o, defining_class)) {
193 Py_INCREF(o);
194 return o;
195 }
196
197 Py_INCREF(Py_None);
198 return Py_None;
199}
200
201static PyMethodDef Xxo_methods[] = {
202 {"demo", (PyCFunction)(void(*)(void))Xxo_demo,
203 METH_METHOD | METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("demo(o) -> o")},
204 {NULL, NULL} /* sentinel */
205};
206
207/* Xxo type definition */
208
209PyDoc_STRVAR(Xxo_doc,
210 "A class that explicitly stores attributes in an internal dict");
211
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000212static PyType_Slot Xxo_Type_slots[] = {
Petr Viktorinc168b502020-12-08 17:36:53 +0100213 {Py_tp_doc, (char *)Xxo_doc},
Nick Coghlan53f95022015-06-04 21:52:57 +1000214 {Py_tp_traverse, Xxo_traverse},
215 {Py_tp_finalize, Xxo_finalize},
Petr Viktorinc168b502020-12-08 17:36:53 +0100216 {Py_tp_dealloc, Xxo_dealloc},
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000217 {Py_tp_getattro, Xxo_getattro},
Petr Viktorinc168b502020-12-08 17:36:53 +0100218 {Py_tp_setattro, Xxo_setattro},
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000219 {Py_tp_methods, Xxo_methods},
Petr Viktorinc168b502020-12-08 17:36:53 +0100220 {0, 0}, /* sentinel */
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000221};
222
223static PyType_Spec Xxo_Type_spec = {
Petr Viktorinc168b502020-12-08 17:36:53 +0100224 .name = "xxlimited.Xxo",
225 .basicsize = sizeof(XxoObject),
226 .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
227 .slots = Xxo_Type_slots,
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000228};
229
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000230
Petr Viktorinc168b502020-12-08 17:36:53 +0100231/* Str type definition*/
232
233static PyType_Slot Str_Type_slots[] = {
234 {0, 0}, /* sentinel */
235};
236
237static PyType_Spec Str_Type_spec = {
238 .name = "xxlimited.Str",
239 .basicsize = 0,
240 .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
241 .slots = Str_Type_slots,
242};
243
244
245/* Function of two integers returning integer (with C "long int" arithmetic) */
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000246
247PyDoc_STRVAR(xx_foo_doc,
248"foo(i,j)\n\
249\n\
250Return the sum of i and j.");
251
252static PyObject *
Petr Viktorinc168b502020-12-08 17:36:53 +0100253xx_foo(PyObject *module, PyObject *args)
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000254{
255 long i, j;
256 long res;
257 if (!PyArg_ParseTuple(args, "ll:foo", &i, &j))
258 return NULL;
259 res = i+j; /* XXX Do something here */
260 return PyLong_FromLong(res);
261}
262
263
264/* Function of no arguments returning new Xxo object */
265
266static PyObject *
Petr Viktorinc168b502020-12-08 17:36:53 +0100267xx_new(PyObject *module, PyObject *Py_UNUSED(unused))
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000268{
269 XxoObject *rv;
270
Petr Viktorinc168b502020-12-08 17:36:53 +0100271 rv = newXxoObject(module);
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000272 if (rv == NULL)
273 return NULL;
274 return (PyObject *)rv;
275}
276
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000277
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000278
279/* List of functions defined in the module */
280
281static PyMethodDef xx_methods[] = {
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000282 {"foo", xx_foo, METH_VARARGS,
283 xx_foo_doc},
Petr Viktorinc168b502020-12-08 17:36:53 +0100284 {"new", xx_new, METH_NOARGS,
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000285 PyDoc_STR("new() -> new Xx object")},
286 {NULL, NULL} /* sentinel */
287};
288
Petr Viktorinc168b502020-12-08 17:36:53 +0100289
290/* The module itself */
291
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000292PyDoc_STRVAR(module_doc,
293"This is a template module just for instruction.");
294
Nick Coghland5cacbb2015-05-23 22:24:10 +1000295static int
296xx_modexec(PyObject *m)
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000297{
Petr Viktorinc168b502020-12-08 17:36:53 +0100298 xx_state *state = PyModule_GetState(m);
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000299
Petr Viktorinc168b502020-12-08 17:36:53 +0100300 state->Error_Type = PyErr_NewException("xxlimited.Error", NULL, NULL);
301 if (state->Error_Type == NULL) {
302 return -1;
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000303 }
Petr Viktorinc168b502020-12-08 17:36:53 +0100304 if (PyModule_AddType(m, (PyTypeObject*)state->Error_Type) < 0) {
305 return -1;
306 }
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000307
Petr Viktorinc168b502020-12-08 17:36:53 +0100308 state->Xxo_Type = PyType_FromModuleAndSpec(m, &Xxo_Type_spec, NULL);
309 if (state->Xxo_Type == NULL) {
310 return -1;
311 }
312 if (PyModule_AddType(m, (PyTypeObject*)state->Xxo_Type) < 0) {
313 return -1;
314 }
Nick Coghlan53f95022015-06-04 21:52:57 +1000315
Petr Viktorinc168b502020-12-08 17:36:53 +0100316 // Add the Str type. It is not needed from C code, so it is only
317 // added to the module dict.
318 // It does not inherit from "object" (PyObject_Type), but from "str"
319 // (PyUnincode_Type).
320 PyObject *Str_Type = PyType_FromModuleAndSpec(
321 m, &Str_Type_spec, (PyObject *)&PyUnicode_Type);
322 if (Str_Type == NULL) {
323 return -1;
324 }
325 if (PyModule_AddType(m, (PyTypeObject*)Str_Type) < 0) {
326 return -1;
327 }
328 Py_DECREF(Str_Type);
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000329
Nick Coghland5cacbb2015-05-23 22:24:10 +1000330 return 0;
Nick Coghland5cacbb2015-05-23 22:24:10 +1000331}
332
Nick Coghland5cacbb2015-05-23 22:24:10 +1000333static PyModuleDef_Slot xx_slots[] = {
334 {Py_mod_exec, xx_modexec},
335 {0, NULL}
336};
337
Petr Viktorinc168b502020-12-08 17:36:53 +0100338static int
339xx_traverse(PyObject *module, visitproc visit, void *arg)
340{
341 xx_state *state = PyModule_GetState(module);
342 Py_VISIT(state->Xxo_Type);
343 Py_VISIT(state->Error_Type);
344 return 0;
345}
346
347static int
348xx_clear(PyObject *module)
349{
350 xx_state *state = PyModule_GetState(module);
351 Py_CLEAR(state->Xxo_Type);
352 Py_CLEAR(state->Error_Type);
353 return 0;
354}
355
Nick Coghland5cacbb2015-05-23 22:24:10 +1000356static struct PyModuleDef xxmodule = {
357 PyModuleDef_HEAD_INIT,
Petr Viktorinc168b502020-12-08 17:36:53 +0100358 .m_name = "xxlimited",
359 .m_doc = module_doc,
360 .m_size = sizeof(xx_state),
361 .m_methods = xx_methods,
362 .m_slots = xx_slots,
363 .m_traverse = xx_traverse,
364 .m_clear = xx_clear,
365 /* m_free is not necessary here: xx_clear clears all references,
366 * and the module state is deallocated along with the module.
367 */
Nick Coghland5cacbb2015-05-23 22:24:10 +1000368};
369
Petr Viktorinc168b502020-12-08 17:36:53 +0100370
Nick Coghland5cacbb2015-05-23 22:24:10 +1000371/* Export function for the module (*must* be called PyInit_xx) */
372
373PyMODINIT_FUNC
374PyInit_xxlimited(void)
375{
376 return PyModuleDef_Init(&xxmodule);
Martin v. Löwis4d0d4712010-12-03 20:14:31 +0000377}