1 /* Use this file as a template to start implementing a module that
2 also declares object types. All occurrences of 'Xxo' should be changed
3 to something reasonable for your objects. After that, all other
4 occurrences of 'xx' should be changed to something reasonable for your
5 module. If your module is named foo your source file should be named
6 foo.c or foomodule.c.
7
8 You will probably want to delete all references to 'x_attr' and add
9 your own types of attributes instead. Maybe you want to name your
10 local variables other than 'self'. If your object type is needed in
11 other files, you'll have to create a file "foobarobject.h"; see
12 floatobject.h for an example.
13
14 This module roughly corresponds to::
15
16 class Xxo:
17 """A class that explicitly stores attributes in an internal dict"""
18
19 def __init__(self):
20 # In the C class, "_x_attr" is not accessible from Python code
21 self._x_attr = {}
22 self._x_exports = 0
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 @property
34 def x_exports(self):
35 """Return the number of times an internal buffer is exported."""
36 # Each Xxo instance has a 10-byte buffer that can be
37 # accessed via the buffer interface (e.g. `memoryview`).
38 return self._x_exports
39
40 def demo(o, /):
41 if isinstance(o, str):
42 return o
43 elif isinstance(o, Xxo):
44 return o
45 else:
46 raise Error('argument must be str or Xxo')
47
48 class Error(Exception):
49 """Exception raised by the xxlimited module"""
50
51 def foo(i: int, j: int, /):
52 """Return the sum of i and j."""
53 # Unlike this pseudocode, the C function will *only* work with
54 # integers and perform C long int arithmetic
55 return i + j
56
57 def new():
58 return Xxo()
59
60 def Str(str):
61 # A trivial subclass of a built-in type
62 pass
63 */
64
65 #define Py_LIMITED_API 0x030b0000
66
67 #include "Python.h"
68 #include <string.h>
69
70 #define BUFSIZE 10
71
72 // Module state
73 typedef struct {
74 PyObject *Xxo_Type; // Xxo class
75 PyObject *Error_Type; // Error class
76 } xx_state;
77
78
79 /* Xxo objects */
80
81 // Instance state
82 typedef struct {
83 PyObject_HEAD
84 PyObject *x_attr; /* Attributes dictionary */
85 char x_buffer[BUFSIZE]; /* buffer for Py_buffer */
86 Py_ssize_t x_exports; /* how many buffer are exported */
87 } XxoObject;
88
89 // XXX: no good way to do this yet
90 // #define XxoObject_Check(v) Py_IS_TYPE(v, Xxo_Type)
91
92 static XxoObject *
93 newXxoObject(PyObject *module)
94 {
95 xx_state *state = PyModule_GetState(module);
96 if (state == NULL) {
97 return NULL;
98 }
99 XxoObject *self;
100 self = PyObject_GC_New(XxoObject, (PyTypeObject*)state->Xxo_Type);
101 if (self == NULL) {
102 return NULL;
103 }
104 self->x_attr = NULL;
105 memset(self->x_buffer, 0, BUFSIZE);
106 self->x_exports = 0;
107 return self;
108 }
109
110 /* Xxo finalization */
111
112 static int
113 Xxo_traverse(PyObject *self_obj, visitproc visit, void *arg)
114 {
115 // Visit the type
116 Py_VISIT(Py_TYPE(self_obj));
117
118 // Visit the attribute dict
119 XxoObject *self = (XxoObject *)self_obj;
120 Py_VISIT(self->x_attr);
121 return 0;
122 }
123
124 static int
125 Xxo_clear(XxoObject *self)
126 {
127 Py_CLEAR(self->x_attr);
128 return 0;
129 }
130
131 static void
132 Xxo_finalize(PyObject *self_obj)
133 {
134 XxoObject *self = (XxoObject *)self_obj;
135 Py_CLEAR(self->x_attr);
136 }
137
138 static void
139 Xxo_dealloc(PyObject *self)
140 {
141 PyObject_GC_UnTrack(self);
142 Xxo_finalize(self);
143 PyTypeObject *tp = Py_TYPE(self);
144 freefunc free = PyType_GetSlot(tp, Py_tp_free);
145 free(self);
146 Py_DECREF(tp);
147 }
148
149
150 /* Xxo attribute handling */
151
152 static PyObject *
153 Xxo_getattro(XxoObject *self, PyObject *name)
154 {
155 if (self->x_attr != NULL) {
156 PyObject *v = PyDict_GetItemWithError(self->x_attr, name);
157 if (v != NULL) {
158 return Py_NewRef(v);
159 }
160 else if (PyErr_Occurred()) {
161 return NULL;
162 }
163 }
164 return PyObject_GenericGetAttr((PyObject *)self, name);
165 }
166
167 static int
168 Xxo_setattro(XxoObject *self, PyObject *name, PyObject *v)
169 {
170 if (self->x_attr == NULL) {
171 // prepare the attribute dict
172 self->x_attr = PyDict_New();
173 if (self->x_attr == NULL) {
174 return -1;
175 }
176 }
177 if (v == NULL) {
178 // delete an attribute
179 int rv = PyDict_DelItem(self->x_attr, name);
180 if (rv < 0 && PyErr_ExceptionMatches(PyExc_KeyError)) {
181 PyErr_SetString(PyExc_AttributeError,
182 "delete non-existing Xxo attribute");
183 return -1;
184 }
185 return rv;
186 }
187 else {
188 // set an attribute
189 return PyDict_SetItem(self->x_attr, name, v);
190 }
191 }
192
193 /* Xxo methods */
194
195 static PyObject *
196 Xxo_demo(XxoObject *self, PyTypeObject *defining_class,
197 PyObject **args, Py_ssize_t nargs, PyObject *kwnames)
198 {
199 if (kwnames != NULL && PyObject_Length(kwnames)) {
200 PyErr_SetString(PyExc_TypeError, "demo() takes no keyword arguments");
201 return NULL;
202 }
203 if (nargs != 1) {
204 PyErr_SetString(PyExc_TypeError, "demo() takes exactly 1 argument");
205 return NULL;
206 }
207
208 PyObject *o = args[0];
209
210 /* Test if the argument is "str" */
211 if (PyUnicode_Check(o)) {
212 return Py_NewRef(o);
213 }
214
215 /* test if the argument is of the Xxo class */
216 if (PyObject_TypeCheck(o, defining_class)) {
217 return Py_NewRef(o);
218 }
219
220 return Py_NewRef(Py_None);
221 }
222
223 static PyMethodDef Xxo_methods[] = {
224 {"demo", _PyCFunction_CAST(Xxo_demo),
225 METH_METHOD | METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("demo(o) -> o")},
226 {NULL, NULL} /* sentinel */
227 };
228
229 /* Xxo buffer interface */
230
231 static int
232 Xxo_getbuffer(XxoObject *self, Py_buffer *view, int flags)
233 {
234 int res = PyBuffer_FillInfo(view, (PyObject*)self,
235 (void *)self->x_buffer, BUFSIZE,
236 0, flags);
237 if (res == 0) {
238 self->x_exports++;
239 }
240 return res;
241 }
242
243 static void
244 Xxo_releasebuffer(XxoObject *self, Py_buffer *view)
245 {
246 self->x_exports--;
247 }
248
249 static PyObject *
250 Xxo_get_x_exports(XxoObject *self, void *c)
251 {
252 return PyLong_FromSsize_t(self->x_exports);
253 }
254
255 /* Xxo type definition */
256
257 PyDoc_STRVAR(Xxo_doc,
258 "A class that explicitly stores attributes in an internal dict");
259
260 static PyGetSetDef Xxo_getsetlist[] = {
261 {"x_exports", (getter) Xxo_get_x_exports, NULL, NULL},
262 {NULL},
263 };
264
265
266 static PyType_Slot Xxo_Type_slots[] = {
267 {Py_tp_doc, (char *)Xxo_doc},
268 {Py_tp_traverse, Xxo_traverse},
269 {Py_tp_clear, Xxo_clear},
270 {Py_tp_finalize, Xxo_finalize},
271 {Py_tp_dealloc, Xxo_dealloc},
272 {Py_tp_getattro, Xxo_getattro},
273 {Py_tp_setattro, Xxo_setattro},
274 {Py_tp_methods, Xxo_methods},
275 {Py_bf_getbuffer, Xxo_getbuffer},
276 {Py_bf_releasebuffer, Xxo_releasebuffer},
277 {Py_tp_getset, Xxo_getsetlist},
278 {0, 0}, /* sentinel */
279 };
280
281 static PyType_Spec Xxo_Type_spec = {
282 .name = "xxlimited.Xxo",
283 .basicsize = sizeof(XxoObject),
284 .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
285 .slots = Xxo_Type_slots,
286 };
287
288
289 /* Str type definition*/
290
291 static PyType_Slot Str_Type_slots[] = {
292 {0, 0}, /* sentinel */
293 };
294
295 static PyType_Spec Str_Type_spec = {
296 .name = "xxlimited.Str",
297 .basicsize = 0,
298 .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
299 .slots = Str_Type_slots,
300 };
301
302
303 /* Function of two integers returning integer (with C "long int" arithmetic) */
304
305 PyDoc_STRVAR(xx_foo_doc,
306 "foo(i,j)\n\
307 \n\
308 Return the sum of i and j.");
309
310 static PyObject *
311 xx_foo(PyObject *module, PyObject *args)
312 {
313 long i, j;
314 long res;
315 if (!PyArg_ParseTuple(args, "ll:foo", &i, &j))
316 return NULL;
317 res = i+j; /* XXX Do something here */
318 return PyLong_FromLong(res);
319 }
320
321
322 /* Function of no arguments returning new Xxo object */
323
324 static PyObject *
325 xx_new(PyObject *module, PyObject *Py_UNUSED(unused))
326 {
327 XxoObject *rv;
328
329 rv = newXxoObject(module);
330 if (rv == NULL)
331 return NULL;
332 return (PyObject *)rv;
333 }
334
335
336
337 /* List of functions defined in the module */
338
339 static PyMethodDef xx_methods[] = {
340 {"foo", xx_foo, METH_VARARGS,
341 xx_foo_doc},
342 {"new", xx_new, METH_NOARGS,
343 PyDoc_STR("new() -> new Xx object")},
344 {NULL, NULL} /* sentinel */
345 };
346
347
348 /* The module itself */
349
350 PyDoc_STRVAR(module_doc,
351 "This is a template module just for instruction.");
352
353 static int
354 xx_modexec(PyObject *m)
355 {
356 xx_state *state = PyModule_GetState(m);
357
358 state->Error_Type = PyErr_NewException("xxlimited.Error", NULL, NULL);
359 if (state->Error_Type == NULL) {
360 return -1;
361 }
362 if (PyModule_AddType(m, (PyTypeObject*)state->Error_Type) < 0) {
363 return -1;
364 }
365
366 state->Xxo_Type = PyType_FromModuleAndSpec(m, &Xxo_Type_spec, NULL);
367 if (state->Xxo_Type == NULL) {
368 return -1;
369 }
370 if (PyModule_AddType(m, (PyTypeObject*)state->Xxo_Type) < 0) {
371 return -1;
372 }
373
374 // Add the Str type. It is not needed from C code, so it is only
375 // added to the module dict.
376 // It does not inherit from "object" (PyObject_Type), but from "str"
377 // (PyUnincode_Type).
378 PyObject *Str_Type = PyType_FromModuleAndSpec(
379 m, &Str_Type_spec, (PyObject *)&PyUnicode_Type);
380 if (Str_Type == NULL) {
381 return -1;
382 }
383 if (PyModule_AddType(m, (PyTypeObject*)Str_Type) < 0) {
384 return -1;
385 }
386 Py_DECREF(Str_Type);
387
388 return 0;
389 }
390
391 static PyModuleDef_Slot xx_slots[] = {
392 {Py_mod_exec, xx_modexec},
393 {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
394 {0, NULL}
395 };
396
397 static int
398 xx_traverse(PyObject *module, visitproc visit, void *arg)
399 {
400 xx_state *state = PyModule_GetState(module);
401 Py_VISIT(state->Xxo_Type);
402 Py_VISIT(state->Error_Type);
403 return 0;
404 }
405
406 static int
407 xx_clear(PyObject *module)
408 {
409 xx_state *state = PyModule_GetState(module);
410 Py_CLEAR(state->Xxo_Type);
411 Py_CLEAR(state->Error_Type);
412 return 0;
413 }
414
415 static struct PyModuleDef xxmodule = {
416 PyModuleDef_HEAD_INIT,
417 .m_name = "xxlimited",
418 .m_doc = module_doc,
419 .m_size = sizeof(xx_state),
420 .m_methods = xx_methods,
421 .m_slots = xx_slots,
422 .m_traverse = xx_traverse,
423 .m_clear = xx_clear,
424 /* m_free is not necessary here: xx_clear clears all references,
425 * and the module state is deallocated along with the module.
426 */
427 };
428
429
430 /* Export function for the module (*must* be called PyInit_xx) */
431
432 PyMODINIT_FUNC
433 PyInit_xxlimited(void)
434 {
435 return PyModuleDef_Init(&xxmodule);
436 }