| /* Use this file as a template to start implementing a module that | |
| also declares object types. All occurrences of 'Xxo' should be changed | |
| to something reasonable for your objects. After that, all other | |
| occurrences of 'xx' should be changed to something reasonable for your | |
| module. If your module is named foo your source file should be named | |
| foo.c or foomodule.c. | |
| You will probably want to delete all references to 'x_attr' and add | |
| your own types of attributes instead. Maybe you want to name your | |
| local variables other than 'self'. If your object type is needed in | |
| other files, you'll have to create a file "foobarobject.h"; see | |
| floatobject.h for an example. | |
| This module roughly corresponds to:: | |
| class Xxo: | |
| """A class that explicitly stores attributes in an internal dict""" | |
| def __init__(self): | |
| # In the C class, "_x_attr" is not accessible from Python code | |
| self._x_attr = {} | |
| def __getattr__(self, name): | |
| return self._x_attr[name] | |
| def __setattr__(self, name, value): | |
| self._x_attr[name] = value | |
| def __delattr__(self, name): | |
| del self._x_attr[name] | |
| def demo(o, /): | |
| if isinstance(o, str): | |
| return o | |
| elif isinstance(o, Xxo): | |
| return o | |
| else: | |
| raise Error('argument must be str or Xxo') | |
| class Error(Exception): | |
| """Exception raised by the xxlimited module""" | |
| def foo(i: int, j: int, /): | |
| """Return the sum of i and j.""" | |
| # Unlike this pseudocode, the C function will *only* work with | |
| # integers and perform C long int arithmetic | |
| return i + j | |
| def new(): | |
| return Xxo() | |
| def Str(str): | |
| # A trivial subclass of a built-in type | |
| pass | |
| */ | |
| #define Py_LIMITED_API 0x030a0000 | |
| #include "Python.h" | |
| // Module state | |
| typedef struct { | |
| PyObject *Xxo_Type; // Xxo class | |
| PyObject *Error_Type; // Error class | |
| } xx_state; | |
| /* Xxo objects */ | |
| // Instance state | |
| typedef struct { | |
| PyObject_HEAD | |
| PyObject *x_attr; /* Attributes dictionary */ | |
| } XxoObject; | |
| // XXX: no good way to do this yet | |
| // #define XxoObject_Check(v) Py_IS_TYPE(v, Xxo_Type) | |
| static XxoObject * | |
| newXxoObject(PyObject *module) | |
| { | |
| xx_state *state = PyModule_GetState(module); | |
| if (state == NULL) { | |
| return NULL; | |
| } | |
| XxoObject *self; | |
| self = PyObject_GC_New(XxoObject, (PyTypeObject*)state->Xxo_Type); | |
| if (self == NULL) { | |
| return NULL; | |
| } | |
| self->x_attr = NULL; | |
| return self; | |
| } | |
| /* Xxo finalization */ | |
| static int | |
| Xxo_traverse(XxoObject *self, visitproc visit, void *arg) | |
| { | |
| // Visit the type | |
| Py_VISIT(Py_TYPE(self)); | |
| // Visit the attribute dict | |
| Py_VISIT(self->x_attr); | |
| return 0; | |
| } | |
| static int | |
| Xxo_clear(XxoObject *self) | |
| { | |
| Py_CLEAR(self->x_attr); | |
| return 0; | |
| } | |
| static void | |
| Xxo_finalize(XxoObject *self) | |
| { | |
| Py_CLEAR(self->x_attr); | |
| } | |
| static void | |
| Xxo_dealloc(XxoObject *self) | |
| { | |
| Xxo_finalize(self); | |
| PyTypeObject *tp = Py_TYPE(self); | |
| freefunc free = PyType_GetSlot(tp, Py_tp_free); | |
| free(self); | |
| Py_DECREF(tp); | |
| } | |
| /* Xxo attribute handling */ | |
| static PyObject * | |
| Xxo_getattro(XxoObject *self, PyObject *name) | |
| { | |
| if (self->x_attr != NULL) { | |
| PyObject *v = PyDict_GetItemWithError(self->x_attr, name); | |
| if (v != NULL) { | |
| Py_INCREF(v); | |
| return v; | |
| } | |
| else if (PyErr_Occurred()) { | |
| return NULL; | |
| } | |
| } | |
| return PyObject_GenericGetAttr((PyObject *)self, name); | |
| } | |
| static int | |
| Xxo_setattro(XxoObject *self, PyObject *name, PyObject *v) | |
| { | |
| if (self->x_attr == NULL) { | |
| // prepare the attribute dict | |
| self->x_attr = PyDict_New(); | |
| if (self->x_attr == NULL) { | |
| return -1; | |
| } | |
| } | |
| if (v == NULL) { | |
| // delete an attribute | |
| int rv = PyDict_DelItem(self->x_attr, name); | |
| if (rv < 0 && PyErr_ExceptionMatches(PyExc_KeyError)) { | |
| PyErr_SetString(PyExc_AttributeError, | |
| "delete non-existing Xxo attribute"); | |
| return -1; | |
| } | |
| return rv; | |
| } | |
| else { | |
| // set an attribute | |
| return PyDict_SetItem(self->x_attr, name, v); | |
| } | |
| } | |
| /* Xxo methods */ | |
| static PyObject * | |
| Xxo_demo(XxoObject *self, PyTypeObject *defining_class, | |
| PyObject **args, Py_ssize_t nargs, PyObject *kwnames) | |
| { | |
| if (kwnames != NULL && PyObject_Length(kwnames)) { | |
| PyErr_SetString(PyExc_TypeError, "demo() takes no keyword arguments"); | |
| return NULL; | |
| } | |
| if (nargs != 1) { | |
| PyErr_SetString(PyExc_TypeError, "demo() takes exactly 1 argument"); | |
| return NULL; | |
| } | |
| PyObject *o = args[0]; | |
| /* Test if the argument is "str" */ | |
| if (PyUnicode_Check(o)) { | |
| Py_INCREF(o); | |
| return o; | |
| } | |
| /* test if the argument is of the Xxo class */ | |
| if (PyObject_TypeCheck(o, defining_class)) { | |
| Py_INCREF(o); | |
| return o; | |
| } | |
| Py_INCREF(Py_None); | |
| return Py_None; | |
| } | |
| static PyMethodDef Xxo_methods[] = { | |
| {"demo", (PyCFunction)(void(*)(void))Xxo_demo, | |
| METH_METHOD | METH_FASTCALL | METH_KEYWORDS, PyDoc_STR("demo(o) -> o")}, | |
| {NULL, NULL} /* sentinel */ | |
| }; | |
| /* Xxo type definition */ | |
| PyDoc_STRVAR(Xxo_doc, | |
| "A class that explicitly stores attributes in an internal dict"); | |
| static PyType_Slot Xxo_Type_slots[] = { | |
| {Py_tp_doc, (char *)Xxo_doc}, | |
| {Py_tp_traverse, Xxo_traverse}, | |
| {Py_tp_clear, Xxo_clear}, | |
| {Py_tp_finalize, Xxo_finalize}, | |
| {Py_tp_dealloc, Xxo_dealloc}, | |
| {Py_tp_getattro, Xxo_getattro}, | |
| {Py_tp_setattro, Xxo_setattro}, | |
| {Py_tp_methods, Xxo_methods}, | |
| {0, 0}, /* sentinel */ | |
| }; | |
| static PyType_Spec Xxo_Type_spec = { | |
| .name = "xxlimited.Xxo", | |
| .basicsize = sizeof(XxoObject), | |
| .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, | |
| .slots = Xxo_Type_slots, | |
| }; | |
| /* Str type definition*/ | |
| static PyType_Slot Str_Type_slots[] = { | |
| {0, 0}, /* sentinel */ | |
| }; | |
| static PyType_Spec Str_Type_spec = { | |
| .name = "xxlimited.Str", | |
| .basicsize = 0, | |
| .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, | |
| .slots = Str_Type_slots, | |
| }; | |
| /* Function of two integers returning integer (with C "long int" arithmetic) */ | |
| PyDoc_STRVAR(xx_foo_doc, | |
| "foo(i,j)\n\ | |
| \n\ | |
| Return the sum of i and j."); | |
| static PyObject * | |
| xx_foo(PyObject *module, PyObject *args) | |
| { | |
| long i, j; | |
| long res; | |
| if (!PyArg_ParseTuple(args, "ll:foo", &i, &j)) | |
| return NULL; | |
| res = i+j; /* XXX Do something here */ | |
| return PyLong_FromLong(res); | |
| } | |
| /* Function of no arguments returning new Xxo object */ | |
| static PyObject * | |
| xx_new(PyObject *module, PyObject *Py_UNUSED(unused)) | |
| { | |
| XxoObject *rv; | |
| rv = newXxoObject(module); | |
| if (rv == NULL) | |
| return NULL; | |
| return (PyObject *)rv; | |
| } | |
| /* List of functions defined in the module */ | |
| static PyMethodDef xx_methods[] = { | |
| {"foo", xx_foo, METH_VARARGS, | |
| xx_foo_doc}, | |
| {"new", xx_new, METH_NOARGS, | |
| PyDoc_STR("new() -> new Xx object")}, | |
| {NULL, NULL} /* sentinel */ | |
| }; | |
| /* The module itself */ | |
| PyDoc_STRVAR(module_doc, | |
| "This is a template module just for instruction."); | |
| static int | |
| xx_modexec(PyObject *m) | |
| { | |
| xx_state *state = PyModule_GetState(m); | |
| state->Error_Type = PyErr_NewException("xxlimited.Error", NULL, NULL); | |
| if (state->Error_Type == NULL) { | |
| return -1; | |
| } | |
| if (PyModule_AddType(m, (PyTypeObject*)state->Error_Type) < 0) { | |
| return -1; | |
| } | |
| state->Xxo_Type = PyType_FromModuleAndSpec(m, &Xxo_Type_spec, NULL); | |
| if (state->Xxo_Type == NULL) { | |
| return -1; | |
| } | |
| if (PyModule_AddType(m, (PyTypeObject*)state->Xxo_Type) < 0) { | |
| return -1; | |
| } | |
| // Add the Str type. It is not needed from C code, so it is only | |
| // added to the module dict. | |
| // It does not inherit from "object" (PyObject_Type), but from "str" | |
| // (PyUnincode_Type). | |
| PyObject *Str_Type = PyType_FromModuleAndSpec( | |
| m, &Str_Type_spec, (PyObject *)&PyUnicode_Type); | |
| if (Str_Type == NULL) { | |
| return -1; | |
| } | |
| if (PyModule_AddType(m, (PyTypeObject*)Str_Type) < 0) { | |
| return -1; | |
| } | |
| Py_DECREF(Str_Type); | |
| return 0; | |
| } | |
| static PyModuleDef_Slot xx_slots[] = { | |
| {Py_mod_exec, xx_modexec}, | |
| {0, NULL} | |
| }; | |
| static int | |
| xx_traverse(PyObject *module, visitproc visit, void *arg) | |
| { | |
| xx_state *state = PyModule_GetState(module); | |
| Py_VISIT(state->Xxo_Type); | |
| Py_VISIT(state->Error_Type); | |
| return 0; | |
| } | |
| static int | |
| xx_clear(PyObject *module) | |
| { | |
| xx_state *state = PyModule_GetState(module); | |
| Py_CLEAR(state->Xxo_Type); | |
| Py_CLEAR(state->Error_Type); | |
| return 0; | |
| } | |
| static struct PyModuleDef xxmodule = { | |
| PyModuleDef_HEAD_INIT, | |
| .m_name = "xxlimited", | |
| .m_doc = module_doc, | |
| .m_size = sizeof(xx_state), | |
| .m_methods = xx_methods, | |
| .m_slots = xx_slots, | |
| .m_traverse = xx_traverse, | |
| .m_clear = xx_clear, | |
| /* m_free is not necessary here: xx_clear clears all references, | |
| * and the module state is deallocated along with the module. | |
| */ | |
| }; | |
| /* Export function for the module (*must* be called PyInit_xx) */ | |
| PyMODINIT_FUNC | |
| PyInit_xxlimited(void) | |
| { | |
| return PyModuleDef_Init(&xxmodule); | |
| } |