1 | |
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 |
6 | module. If your module is named foo your source file should be named |
7 | foo.c or foomodule.c. |
8 | |
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 |
13 | floatobject.h for an example. |
14 | |
15 | 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 | */ |
57 | |
58 | #define Py_LIMITED_API 0x030a0000 |
59 | |
60 | #include "Python.h" |
61 | |
62 | // Module state |
63 | typedef struct { |
64 | PyObject *Xxo_Type; // Xxo class |
65 | PyObject *Error_Type; // Error class |
66 | } xx_state; |
67 | |
68 | |
69 | /* Xxo objects */ |
70 | |
71 | // Instance state |
72 | typedef struct { |
73 | PyObject_HEAD |
74 | PyObject *x_attr; /* Attributes dictionary */ |
75 | } XxoObject; |
76 | |
77 | // XXX: no good way to do this yet |
78 | // #define XxoObject_Check(v) Py_IS_TYPE(v, Xxo_Type) |
79 | |
80 | static XxoObject * |
81 | newXxoObject(PyObject *module) |
82 | { |
83 | xx_state *state = PyModule_GetState(module); |
84 | if (state == NULL) { |
85 | return NULL; |
86 | } |
87 | XxoObject *self; |
88 | self = PyObject_GC_New(XxoObject, (PyTypeObject*)state->Xxo_Type); |
89 | if (self == NULL) { |
90 | return NULL; |
91 | } |
92 | self->x_attr = NULL; |
93 | return self; |
94 | } |
95 | |
96 | /* Xxo finalization */ |
97 | |
98 | static int |
99 | Xxo_traverse(XxoObject *self, visitproc visit, void *arg) |
100 | { |
101 | // Visit the type |
102 | Py_VISIT(Py_TYPE(self)); |
103 | |
104 | // Visit the attribute dict |
105 | Py_VISIT(self->x_attr); |
106 | return 0; |
107 | } |
108 | |
109 | static int |
110 | Xxo_clear(XxoObject *self) |
111 | { |
112 | Py_CLEAR(self->x_attr); |
113 | return 0; |
114 | } |
115 | |
116 | static void |
117 | Xxo_finalize(XxoObject *self) |
118 | { |
119 | Py_CLEAR(self->x_attr); |
120 | } |
121 | |
122 | static void |
123 | Xxo_dealloc(XxoObject *self) |
124 | { |
125 | 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); |
130 | } |
131 | |
132 | |
133 | /* Xxo attribute handling */ |
134 | |
135 | static PyObject * |
136 | Xxo_getattro(XxoObject *self, PyObject *name) |
137 | { |
138 | if (self->x_attr != NULL) { |
139 | PyObject *v = PyDict_GetItemWithError(self->x_attr, name); |
140 | if (v != NULL) { |
141 | Py_INCREF(v); |
142 | return v; |
143 | } |
144 | else if (PyErr_Occurred()) { |
145 | return NULL; |
146 | } |
147 | } |
148 | return PyObject_GenericGetAttr((PyObject *)self, name); |
149 | } |
150 | |
151 | static int |
152 | Xxo_setattro(XxoObject *self, PyObject *name, PyObject *v) |
153 | { |
154 | if (self->x_attr == NULL) { |
155 | // prepare the attribute dict |
156 | self->x_attr = PyDict_New(); |
157 | if (self->x_attr == NULL) { |
158 | return -1; |
159 | } |
160 | } |
161 | if (v == NULL) { |
162 | // delete an attribute |
163 | int rv = PyDict_DelItem(self->x_attr, name); |
164 | if (rv < 0 && PyErr_ExceptionMatches(PyExc_KeyError)) { |
165 | PyErr_SetString(PyExc_AttributeError, |
166 | "delete non-existing Xxo attribute" ); |
167 | return -1; |
168 | } |
169 | return rv; |
170 | } |
171 | else { |
172 | // set an attribute |
173 | return PyDict_SetItem(self->x_attr, name, v); |
174 | } |
175 | } |
176 | |
177 | /* Xxo methods */ |
178 | |
179 | static PyObject * |
180 | Xxo_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 | |
210 | static 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 | |
218 | PyDoc_STRVAR(Xxo_doc, |
219 | "A class that explicitly stores attributes in an internal dict" ); |
220 | |
221 | static PyType_Slot Xxo_Type_slots[] = { |
222 | {Py_tp_doc, (char *)Xxo_doc}, |
223 | {Py_tp_traverse, Xxo_traverse}, |
224 | {Py_tp_clear, Xxo_clear}, |
225 | {Py_tp_finalize, Xxo_finalize}, |
226 | {Py_tp_dealloc, Xxo_dealloc}, |
227 | {Py_tp_getattro, Xxo_getattro}, |
228 | {Py_tp_setattro, Xxo_setattro}, |
229 | {Py_tp_methods, Xxo_methods}, |
230 | {0, 0}, /* sentinel */ |
231 | }; |
232 | |
233 | static PyType_Spec Xxo_Type_spec = { |
234 | .name = "xxlimited.Xxo" , |
235 | .basicsize = sizeof(XxoObject), |
236 | .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, |
237 | .slots = Xxo_Type_slots, |
238 | }; |
239 | |
240 | |
241 | /* Str type definition*/ |
242 | |
243 | static PyType_Slot Str_Type_slots[] = { |
244 | {0, 0}, /* sentinel */ |
245 | }; |
246 | |
247 | static 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) */ |
256 | |
257 | PyDoc_STRVAR(xx_foo_doc, |
258 | "foo(i,j)\n\ |
259 | \n\ |
260 | Return the sum of i and j." ); |
261 | |
262 | static PyObject * |
263 | xx_foo(PyObject *module, PyObject *args) |
264 | { |
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 | |
276 | static PyObject * |
277 | xx_new(PyObject *module, PyObject *Py_UNUSED(unused)) |
278 | { |
279 | XxoObject *rv; |
280 | |
281 | rv = newXxoObject(module); |
282 | if (rv == NULL) |
283 | return NULL; |
284 | return (PyObject *)rv; |
285 | } |
286 | |
287 | |
288 | |
289 | /* List of functions defined in the module */ |
290 | |
291 | static PyMethodDef xx_methods[] = { |
292 | {"foo" , xx_foo, METH_VARARGS, |
293 | xx_foo_doc}, |
294 | {"new" , xx_new, METH_NOARGS, |
295 | PyDoc_STR("new() -> new Xx object" )}, |
296 | {NULL, NULL} /* sentinel */ |
297 | }; |
298 | |
299 | |
300 | /* The module itself */ |
301 | |
302 | PyDoc_STRVAR(module_doc, |
303 | "This is a template module just for instruction." ); |
304 | |
305 | static int |
306 | xx_modexec(PyObject *m) |
307 | { |
308 | xx_state *state = PyModule_GetState(m); |
309 | |
310 | state->Error_Type = PyErr_NewException("xxlimited.Error" , NULL, NULL); |
311 | if (state->Error_Type == NULL) { |
312 | return -1; |
313 | } |
314 | if (PyModule_AddType(m, (PyTypeObject*)state->Error_Type) < 0) { |
315 | return -1; |
316 | } |
317 | |
318 | 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 | } |
325 | |
326 | // 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); |
339 | |
340 | return 0; |
341 | } |
342 | |
343 | static PyModuleDef_Slot xx_slots[] = { |
344 | {Py_mod_exec, xx_modexec}, |
345 | {0, NULL} |
346 | }; |
347 | |
348 | static int |
349 | xx_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 | |
357 | static int |
358 | xx_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 | |
366 | static struct PyModuleDef xxmodule = { |
367 | PyModuleDef_HEAD_INIT, |
368 | .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 | */ |
378 | }; |
379 | |
380 | |
381 | /* Export function for the module (*must* be called PyInit_xx) */ |
382 | |
383 | PyMODINIT_FUNC |
384 | PyInit_xxlimited(void) |
385 | { |
386 | return PyModuleDef_Init(&xxmodule); |
387 | } |
388 | |