• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 *
newXxoObject(PyObject * module)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
Xxo_traverse(XxoObject * self,visitproc visit,void * arg)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
Xxo_clear(XxoObject * self)110 Xxo_clear(XxoObject *self)
111 {
112     Py_CLEAR(self->x_attr);
113     return 0;
114 }
115 
116 static void
Xxo_finalize(XxoObject * self)117 Xxo_finalize(XxoObject *self)
118 {
119     Py_CLEAR(self->x_attr);
120 }
121 
122 static void
Xxo_dealloc(XxoObject * self)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 *
Xxo_getattro(XxoObject * self,PyObject * name)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
Xxo_setattro(XxoObject * self,PyObject * name,PyObject * v)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 *
Xxo_demo(XxoObject * self,PyTypeObject * defining_class,PyObject ** args,Py_ssize_t nargs,PyObject * kwnames)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 *
xx_foo(PyObject * module,PyObject * args)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 *
xx_new(PyObject * module,PyObject * Py_UNUSED (unused))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
xx_modexec(PyObject * m)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
xx_traverse(PyObject * module,visitproc visit,void * arg)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
xx_clear(PyObject * module)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
PyInit_xxlimited(void)384 PyInit_xxlimited(void)
385 {
386     return PyModuleDef_Init(&xxmodule);
387 }
388