Creating a module subclass in a Python extension - python

I am trying to create a Python extension module with multi-phase initialization, following the advice I got from a previous question. PEP 489 suggests that it is preferable for the Py_mod_create function to return a module subclass, which presumably means a subclass of PyModule, but I cannot figure out how to do this. In all my attempts, the module segfaults when it is imported. It works fine if Py_mod_create returns some other object, (one which is not a subclass of PyModule), but I am not sure if this will cause problems in future, since isinstance(mymodule, types.ModuleType) returns false in this case.
Following the docs on subclassing built-in types, I set tp_base to PyModule_Type, and my tp_init function calls PyModule_Type.tp_init. The docs also suggest that my structure should contain the superclass structure at the beginning, which in this case is PyModuleObject. This structure is not in the public Python header files, (it is defined in moduleobject.c in the Python sources), so for now I copied and paste the definitions of the PyModuleObject fields at the start of my structure. The complete code looks like this:
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <stdio.h>
struct testmod_s {
// Fields copied from PyModuleObject in moduleobject.c
PyObject_HEAD
PyObject *md_dict;
struct PyModuleDef *md_def;
void *md_state;
PyObject *md_weaklist;
PyObject *md_name;
};
static int testmod_init(PyObject *self, PyObject *args, PyObject *kwds);
static PyObject *testmod_create(PyObject *spec, PyModuleDef *def);
static PyModuleDef_Slot testmod_slots[] = {
{Py_mod_create, testmod_create},
{0, 0} /* Sentinel */
};
static struct PyModuleDef testmod_def = {
PyModuleDef_HEAD_INIT, /* m_base */
"testmod", /* m_name */
NULL, /* m_doc */
sizeof(struct testmod_s), /* m_size */
NULL, /* m_methods */
testmod_slots, /* m_slots */
NULL, /* m_traverse */
NULL, /* m_clear */
NULL /* m_free */
};
static PyTypeObject testmodtype = {
PyVarObject_HEAD_INIT (NULL, 0)
"testmodtype", /* tp_name */
sizeof (struct testmod_s), /* tp_basicsize */
/* fields omitted for brevity, all set to zero */
Py_TPFLAGS_DEFAULT |
Py_TPFLAGS_BASETYPE, /* tp_flags */
/* fields omitted for brevity, all set to zero */
testmod_init, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
};
PyMODINIT_FUNC
PyInit_testmod(void)
{
testmodtype.tp_base = &PyModule_Type;
if (PyType_Ready(&testmodtype)) {
return NULL;
}
PyObject *moduledef = PyModuleDef_Init(&testmod_def);
if (moduledef == NULL) {
return NULL;
}
return moduledef;
}
static int testmod_init(PyObject *self, PyObject *args, PyObject *kwds)
{
if (PyModule_Type.tp_init((PyObject *)self, args, kwds) < 0) {
return -1;
}
return 0;
}
static PyObject *testmod_create(PyObject *spec, PyModuleDef *def)
{
struct testmod_s *module = PyObject_New(struct testmod_s, &testmodtype);
if (module == NULL) {
return NULL;
}
return (PyObject *) module;
}
Importing this module causes a segfault. What am I doing wrong?
I am running Python 3.8.5 on macOS 12.0.1 with a build from Anaconda:
>>> sys.version
'3.8.5 (default, Sep 4 2020, 02:22:02) \n[Clang 10.0.0 ]'

After some tests I could build a custom module type by copying parts of code from moduleobject.c
Your problem is that your code does create an instance of a subclass of module, but never initializes it and gets random values in key members. Additionaly, modules are expected to be gc collectables, so you have to create your custom module with PyObject_GC_New.
The following code replaces your initial testmod_create function with a full initialization of the module:
...
// copied from moduleobject.c
static int
module_init_dict(struct testmod_s* mod, PyObject* md_dict,
PyObject* name, PyObject* doc)
{
_Py_IDENTIFIER(__name__);
_Py_IDENTIFIER(__doc__);
_Py_IDENTIFIER(__package__);
_Py_IDENTIFIER(__loader__);
_Py_IDENTIFIER(__spec__);
if (md_dict == NULL)
return -1;
if (doc == NULL)
doc = Py_None;
if (_PyDict_SetItemId(md_dict, &PyId___name__, name) != 0)
return -1;
if (_PyDict_SetItemId(md_dict, &PyId___doc__, doc) != 0)
return -1;
if (_PyDict_SetItemId(md_dict, &PyId___package__, Py_None) != 0)
return -1;
if (_PyDict_SetItemId(md_dict, &PyId___loader__, Py_None) != 0)
return -1;
if (_PyDict_SetItemId(md_dict, &PyId___spec__, Py_None) != 0)
return -1;
if (PyUnicode_CheckExact(name)) {
Py_INCREF(name);
Py_XSETREF(mod->md_name, name);
}
return 0;
}
static PyObject* testmod_create(PyObject* spec, PyModuleDef* def)
{
struct testmod_s* module = PyObject_GC_New(struct testmod_s, &testmodtype);
if (module == NULL) {
return NULL;
}
PyObject* name = PyUnicode_FromString("testmod");
if (name == NULL) {
Py_DECREF(module);
return 0;
}
module->md_def = NULL;
module->md_state = NULL;
module->md_weaklist = NULL;
module->md_name = NULL;
module->md_dict = PyDict_New();
int cr = module_init_dict(module, module->md_dict, name, NULL);
Py_DECREF(name);
if (cr != 0) {
Py_DECREF(module);
return NULL;
}
return (PyObject*)module;
}

Related

Dynamic attribute in a Python C module

I have a custom Python module written in C, and I want to add an attribute to the module which is dynamically populated. E.g.:
import mymod
print(mymod.x) # At this point, the value of x is computed
The name of the attribute is known in advance.
From what I understand, this should be possible using descriptors, but it is not working as expected. I implemented a custom type, implemented the tp_descr_get function for the type, and assigned an instance of the type to my module, but the tp_descr_get function is never called.
Here is my test module:
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <stdio.h>
static struct PyModuleDef testmod = {
PyModuleDef_HEAD_INIT,
"testmod",
NULL,
-1
};
typedef struct testattrib_s {
PyObject_HEAD
} testattrib;
static PyObject *testattrib_descr_get(PyObject *self, PyObject *obj, PyObject *type);
static int testattrib_descr_set(PyObject *self, PyObject *obj, PyObject *value);
PyTypeObject testattribtype = {
PyVarObject_HEAD_INIT (NULL, 0)
"testattrib", /* tp_name */
sizeof (testattrib), /* tp_basicsize */
/* lots of zeros omitted for brevity */
testattrib_descr_get, /* tp_descr_get */
testattrib_descr_set /* tp_descr_set */
};
PyMODINIT_FUNC
PyInit_testmod(void)
{
if (PyType_Ready(&testattribtype)) {
return NULL;
}
testattrib *attrib = PyObject_New(testattrib, &testattribtype);
if (attrib == NULL) {
return NULL;
}
PyObject *m = PyModule_Create(&testmod);
if (m == NULL) {
return NULL;
}
if (PyModule_AddObject(m, "myattrib", (PyObject *) attrib)) {
return NULL;
}
return m;
}
static PyObject *testattrib_descr_get(PyObject *self, PyObject *obj, PyObject *type)
{
printf("testattrib_descr_get called\n");
Py_INCREF(self);
return self;
}
static int testattrib_descr_set(PyObject *self, PyObject *obj, PyObject *value)
{
printf("testattrib_descr_set called\n");
return 0;
}
I test it like this:
import testmod
print(testmod.myattrib) # should call tp_descr_get
testmod.myattrib = 1 # should call tp_descr_set
The getter/setter functions are never called. What am I doing wrong?
I am running Python 3.8.5 on macOS 12.0.1 with a build from Anaconda:
>>> sys.version
'3.8.5 (default, Sep 4 2020, 02:22:02) \n[Clang 10.0.0 ]'
Descriptors operate only as attributes on a type. You would have to create your module as an instance of a module subclass equipped with the descriptor. The easiest way to do that is to use the Py_mod_create slot (not to be confused with __slots__).

Equivalent to __all__ in Python C API

I am writing a simple extension module using the C API. Here is a test example:
#include <Python.h>
#include <structmember.h>
typedef struct {
PyObject_HEAD
int i;
} MyObject;
static PyMemberDef my_members[] = {
{"i", T_INT, offsetof(MyObject, i), READONLY, "Some integer"},
{NULL} /* Sentinel */
};
static int MyType_init(MyObject *self, PyObject *args, PyObject *kwds)
{
char *keywords[] = {"i", NULL};
int i = 0;
if(!PyArg_ParseTupleAndKeywords(args, kwds, "|i", keywords, &i)) {
return -1;
}
self->i = i;
return 0;
}
static PyTypeObject MyType = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "_my.MyType",
.tp_doc = "Pointless placeholder class",
.tp_basicsize = sizeof(MyObject),
.tp_itemsize = 0,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
.tp_init = (initproc)MyType_init,
.tp_members = my_members,
};
static PyModuleDef my_module = {
PyModuleDef_HEAD_INIT,
.m_name = "_my",
.m_doc = "My module is undocumented!",
.m_size = -1,
};
/* Global entry point */
PyMODINIT_FUNC PyInit__my(void)
{
PyObject *m = NULL;
PyExc_MyException = PyErr_NewExceptionWithDoc("_my.MyException",
"Indicates that something went horribly wrong.",
NULL, NULL);
if(PyExc_MyException == NULL) {
return NULL;
}
if(PyType_Ready(&MyType) < 0) {
goto err;
}
if((m = PyModule_Create(&my_module)) == NULL) {
goto err;
}
Py_INCREF(&MyType);
PyModule_AddObject(m, "MyType", (PyObject *)&MyType);
PyModule_AddObject(m, "MyException", (PyObject *)PyExc_MyException);
return m;
err:
Py_CLEAR(PyExc_MyException);
return NULL;
}
This module contains a class MyType and an exception MyException. The purpose of this module is to provide the base functionality for some additional code written in Python. I would like to be able to do something like:
my.py
from _my import *
class MyType(MyType):
def __init__(i=0):
raise MyException
Obviously this example is highly contrived. I just want to illustrate how I would like to use the names in my extension. Right now the import is working fine according to this rule:
If __all__ is not defined, the set of public names includes all names found in the module’s namespace which do not begin with an underscore character ('_').
However, I would like to have a finer level of control. So far, all I have been able to come up with is manually adding an __all__ attribute to the module:
PyObject *all = NULL;
...
all = Py_BuildValue("[s, s]", "MyType", "MyException");
if(all == NULL) {
goto err;
}
PyModule_AddObject(m, "__all__", all);
...
err:
...
Py_CLEAR(all);
This seems a bit clunky. Is there a function in the C API for defining an internal equivalent to __all__ for a module?
Part of the reason that I think there may be an internal equivalent is that __all__ is after all a dunder attribute.

python C extensions - argument check in constructor

I have this small python extension that works perfectly except for the fact that arguments of the constructor of class Levtree (which is the levtree_levtree_init function in the C code) are not checked, the function takes a tuple of strings as the only argument, if I call the constructor in a python script with a different argument type or number , segfault is the result, in spite of the usual TypeError, (that works instead for the other method Levtree.search). What's missing?
#include <stdio.h>
#include "levtree.h"
#include <python2.7/Python.h>
#include <python2.7/structmember.h>
typedef struct {
PyObject_HEAD
levtree *tree;
PyObject *wordlist;
/* Type-specific fields go here. */
} levtree_levtree_obj;
static int
levtree_clear(levtree_levtree_obj *self)
{
PyObject *tmp;
tmp = self->wordlist;
self->wordlist = NULL;
Py_XDECREF(tmp);
return 0;
}
static void
levtree_dealloc(levtree_levtree_obj* self)
{
levtree_clear(self);
levtree_free(self->tree);
free(self->tree);
self->ob_type->tp_free((PyObject*)self);
}
static PyObject *
levtree_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
levtree_levtree_obj *self;
self = (levtree_levtree_obj *)type->tp_alloc(type, 0);
if (self != NULL) {
self->tree = NULL;
}
return (PyObject *)self;
}
static int
levtree_levtree_init(levtree_levtree_obj *self, PyObject *args, PyObject *kwds)
{
int numLines; /* how many lines we passed for parsing */
char** carg; /* argument to pass to the C function*/
unsigned i;
PyObject * strObj; /* one string in the list */
/* the O! parses for a Python object (listObj) checked
to be of type PyList_Type */
if (!PyArg_ParseTuple( args, "O!", &PyTuple_Type, &self->wordlist))
{
return -1;
}
Py_INCREF(self->wordlist);
/* get the number of lines passed to us */
numLines = PyTuple_Size(self->wordlist);
carg = malloc(sizeof(char*)*numLines);
/* should raise an error here. */
if (numLines < 0)
{
return -1; /* Not a list */
}
/* iterate over items of the list, grabbing strings, and parsing
for numbers */
for (i=0; i<numLines; i++)
{
/* grab the string object from the next element of the list */
strObj = PyTuple_GetItem(self->wordlist, i); /* Can't fail */
/* make it a string */
carg[i] = PyString_AsString( strObj );
}
self->tree = (levtree*) malloc(sizeof(levtree));
levtree_init(self->tree,carg,numLines);
free(carg);
return 0;
}
static PyObject *
levtree_levtree_search(levtree_levtree_obj* self, PyObject *args, PyObject *kwds)
{
char* wordkey;
index_t number_of_matches=1;
byte_t case_sensitive=0;
index_t i;
PyObject* boolean;
static char *kwlist[] = {"wordkey","number_of_matches","case_sensitive", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|iO", kwlist,
&wordkey, &number_of_matches,&boolean))
{
return NULL;
}
if(PyObject_IsTrue(boolean))
{
case_sensitive=1;
}
if(number_of_matches > self->tree->entry_count) // if some idiot enters a number of results bigger than the list of words given in the constructor
{
number_of_matches = self->tree->entry_count;
}
self->tree->case_sensitive=case_sensitive;
//printf("matches: %u", number_of_matches);
levtree_search(self->tree, wordkey, number_of_matches);
levtree_result res;
PyObject* tmp, *string;
PyObject* list = PyList_New(number_of_matches);
for(i=0; i<number_of_matches; i++)
{
res = levtree_get_result(self->tree,i);
string = PyTuple_GetItem(self->wordlist,res.id);
//printf("%p\t id: %u\n",string,res.id);
tmp = Py_BuildValue("(OI)",string,res.distance);
PyList_SetItem(list,i,tmp);
}
return list;
}
static PyMemberDef Levtree_members[] =
{
// {"standing", T_OBJECT_EX, offsetof(Levtree, ), 0,
// "Match standing"},
{NULL} /* Sentinel */
};
static PyMethodDef Levtree_methods[] =
{
{"search", levtree_levtree_search, METH_KEYWORDS, "Levenshtein tree search method"},
//{"result", levtree_get_result_py, METH_VARARGS, "Levenshtein tree get result method"},
{NULL} /* Sentinel */
};
static PyTypeObject levtree_levtree_type =
{
PyObject_HEAD_INIT(NULL)
0, /*ob_size*/
"levtree.Levtree", /*tp_name*/
sizeof(levtree_levtree_obj), /*tp_basicsize*/
0, /*tp_itemsize*/
(destructor)levtree_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
0, /*tp_call*/
0, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT, /*tp_flags*/
"Levensthein distance tree", /* tp_doc */
0, /* tp_traverse */
(inquiry)levtree_clear, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
Levtree_methods, /* tp_methods */
Levtree_members, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)levtree_levtree_init, /* tp_init */
0, /* tp_alloc */
levtree_new, /* tp_new */
};
#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
#define PyMODINIT_FUNC void
#endif
PyMODINIT_FUNC
initlevtree(void)
{
PyObject* m;
levtree_levtree_type.tp_new = PyType_GenericNew;
if (PyType_Ready(&levtree_levtree_type) < 0)
return;
m = Py_InitModule3("levtree", NULL,
"Example module that creates an extension type.");
Py_INCREF(&levtree_levtree_type);
PyModule_AddObject(m, "Levtree", (PyObject *)&levtree_levtree_type);
}

Python : retrieve variable/result from C module

i have a need to use SPI in Raspberry, and its working with this approach, however its prints the result from the reading of the SPI bus to screen, something i don't want.
Python calls the Compiled C module with the data i want to send, but i want to receive inside python the response, not to the screen.
I never use C and i'm a little shallow in this language, so can someone tell me an option?
i read something about getting cout and fopen but would like to find if there is something more incorporated into python?
Any chance instead of printing the result to screen to do a return result from function or something?
Thank you.
C module:
#include <Python.h>
#include "structmember.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <linux/spi/spidev.h>
#include <linux/types.h>
#include <sys/ioctl.h>
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#define MAXPATH 16
PyDoc_STRVAR(SPI_module_doc,
"This module defines an object type that allows SPI transactions\n"
"on hosts running the Linux kernel. The host kernel must have SPI\n"
"support and SPI device interface support.\n"
"All of these can be either built-in to the kernel, or loaded from\n"
"modules.\n"
"\n"
"Because the SPI device interface is opened R/W, users of this\n"
"module usually must have root permissions.\n");
typedef struct {
PyObject_HEAD
int fd; /* open file descriptor: /dev/spi-X.Y */
uint8_t mode; /* current SPI mode */
uint8_t bpw; /* current SPI bits per word setting */
uint32_t msh; /* current SPI max speed setting in Hz */
} SPI;
static PyObject *
SPI_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
SPI *self;
if ((self = (SPI *)type->tp_alloc(type, 0)) == NULL)
return NULL;
self->fd = -1;
self->mode = 0;
self->bpw = 0;
self->msh = 0;
Py_INCREF(self);
return (PyObject *)self;
}
PyDoc_STRVAR(SPI_close_doc,
"close()\n\n"
"Disconnects the object from the interface.\n");
static PyObject *SPI_close(SPI *self)
{
if ((self->fd != -1) && (close(self->fd) == -1)) {
PyErr_SetFromErrno(PyExc_IOError);
return NULL;
}
self->fd = -1;
self->mode = 0;
self->bpw = 0;
self->msh = 0;
Py_INCREF(Py_None);
return Py_None;
}
PyDoc_STRVAR(SPI_transfer_doc,
"transfer([values]) -> [values]\n\n"
"Perform SPI transaction.\n"
"CS will be released and reactivated between blocks.\n"
"delay specifies delay in usec between blocks.\n");
static PyObject* SPI_transfer(SPI *self, PyObject *args)
{
uint8_t bits = 8;
int ret = 0;
char* list;
int length_list = 1;
uint16_t delay = 5;
uint32_t speed = 500000;
int i=0;
PyArg_ParseTuple(args, "s|i:transfer", &list, &length_list);
char hexbyte[3] = {0};
uint8_t tx[length_list];
for (i=0; i < (length_list); i++){
//should grab first two characters
//Data Transfer from String list to 2 byte string
hexbyte[0] = list[2*i];
hexbyte[1] = list[(2*i)+1];
//Passing the 2 byte string into a Hex unsigned int 8-bit and then printing result
sscanf(hexbyte, "%X", &tx[i]);
}
for (ret=0; ret<ARRAY_SIZE(tx); ret++){
}
puts("\n");
uint8_t rx[ARRAY_SIZE(tx)];
/*This is the transfer part, and sets up
the details needed to transfer the data*/
struct spi_ioc_transfer tr = {
.tx_buf = (unsigned long)tx,
.rx_buf = (unsigned long)rx,
.len = ARRAY_SIZE(tx),
.delay_usecs = delay,
.speed_hz = speed,
.bits_per_word = bits,
};
//The Actuall Transfer command and data, does send and receive!! Very important!
ret = ioctl(self->fd, SPI_IOC_MESSAGE(1), &tr);
if (ret < 1)
printf("ERROR: Can't send spi message");
//This part prints the Received data of the SPI transmission of equal size to TX
for (ret = 0; ret < ARRAY_SIZE(tx); ret++) {
if (!(ret % 6))
puts("");
printf("%.2X ", rx[ret]);
}
puts("");
Py_INCREF(list);
return list;
}
PyDoc_STRVAR(SPI_open_doc,
"open(bus, device)\n\n"
"Connects the object to the specified SPI device.\n"
"open(X,Y) will open /dev/spidev-X.Y\n");
static PyObject *SPI_open(SPI *self, PyObject *args, PyObject *kwds)
{
int bus, device;
char path[MAXPATH];
uint8_t tmp8;
uint32_t tmp32;
static char *kwlist[] = {"bus", "device", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwds, "ii:open", kwlist, &bus, &device))
return NULL;
if (snprintf(path, MAXPATH, "/dev/spidev%d.%d", bus, device) >= MAXPATH) {
PyErr_SetString(PyExc_OverflowError,
"Bus and/or device number is invalid.");
return NULL;
}
if ((self->fd = open(path, O_RDWR, 0)) < 0) {
printf("can't open device");
abort();
}
if (ioctl(self->fd, SPI_IOC_RD_MODE, &tmp8) == -1) {
printf("can't get spi mode");
abort();
}
self->mode = tmp8;
if (ioctl(self->fd, SPI_IOC_RD_BITS_PER_WORD, &tmp8) == -1) {
printf("can't get bits per word");
abort();
}
self->bpw = tmp8;
if (ioctl(self->fd, SPI_IOC_RD_MAX_SPEED_HZ, &tmp32) == -1) {
printf("can't get max speed hz");
abort();
}
self->msh = tmp32;
Py_INCREF(Py_None);
return Py_None;
}
static int SPI_init(SPI *self, PyObject *args, PyObject *kwds)
{
int bus = -1;
int client = -1;
static char *kwlist[] = {"bus", "client", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ii:__init__",
kwlist, &bus, &client))
return -1;
if (bus >= 0) {
SPI_open(self, args, kwds);
if (PyErr_Occurred())
return -1;
}
return 0;
}
static PyMethodDef SPI_module_methods[] = {
{ NULL },
};
PyDoc_STRVAR(SPI_type_doc,
"SPI([bus],[client]) -> SPI\n\n"
"Return a new SPI object that is (optionally) connected to the\n"
"specified SPI device interface.\n");
static PyMethodDef SPI_methods[] = {
{"open", (PyCFunction)SPI_open, METH_VARARGS | METH_KEYWORDS,
SPI_open_doc},
{"close", (PyCFunction)SPI_close, METH_NOARGS,
SPI_close_doc},
{"transfer", (PyCFunction)SPI_transfer, METH_VARARGS,
SPI_transfer_doc},
{NULL},
};
static PyTypeObject SPI_type = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
"SPI", /* tp_name */
sizeof(SPI), /* tp_basicsize */
0, /* tp_itemsize */
0, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
SPI_type_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
SPI_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)SPI_init, /* tp_init */
0, /* tp_alloc */
SPI_new, /* tp_new */
};
#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
#define PyMODINIT_FUNC void
#endif
PyMODINIT_FUNC initspi(void)
{
PyObject* m;
if (PyType_Ready(&SPI_type) < 0)
return;
m = Py_InitModule3("spi", SPI_module_methods, SPI_module_doc);
Py_INCREF(&SPI_type);
PyModule_AddObject(m, "SPI", (PyObject *)&SPI_type);
}
And python script:
#Python example for SPI bus, written by Brian Hensley
#This script will take any amount of Hex values and determine
#the length and then transfer the data as a string to the "spi" module
import spi
from time import sleep
#At the beginning of the program open up the SPI port.
#this is port /dev/spidevX.Y
#Being called as as spi.SPI(X,Y)
a = spi.SPI(0,0)
print "PY: initialising SPI mode, reading data, reading length . . . \n"
#This is my data that I want sent through my SPI bus
data = ["53484f5728677275706f293b3b0d"]
#Calculates the length, and devides by 2 for two bytes of data sent.
length_data = len(data[0])/2
i=0
#transfers data string
while True:
a.transfer(data[0], length_data)
i=i+1
print i
sleep(0.5)
#At the end of your program close the SPI port
a.close()

Python C-Extension segfaults when accessing through tp_getset

I'm trying to write a C-Extension for python. What I'd like to write is a ModPolynomial class which represents a polynomial on (Z/nZ)[x]/x^r-1[even though you may answer to my question without knowing anything of such polynomials].
I've written some code, which seems to work. Basically I just store three PyObject* in my ModPoly structure. Now I'd like to add the storage for the coefficients of the polynomial.
Since I want the coefficients to be read-only, I'd like to add a getter/setter pair of functions through PyGetSetDef. But when I access the getter from python(e.g print pol.coefficients) I receive a Segmentation Fault.
The original code, without the "coefficients" can be found here.
The code with the coefficients is here.
I hope someone of you can tell me where I'm doing wrong here.
By the way, also comments on the code are welcome. This is my first extension and I know that I'm probably doing things quite badly.
As ecatmur says in the comments PyVarObject store a certain number of "slots" at the end of the struct. So I've decided to avoid them.
The relevant code is:
typedef struct {
PyObject_HEAD
/* Type specific fields */
Py_ssize_t ob_size;
PyObject **ob_item;
Py_ssize_t allocated;
PyObject *r_modulus;
PyObject *n_modulus;
PyObject *degree;
} ModPoly;
static PyObject *
ModPoly_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
ModPoly *self;
self = (ModPoly *)type->tp_alloc(type, 0);
if (self != NULL) {
[...]
self->ob_size = 0;
self->ob_item = NULL;
self->allocated = 0;
}
return (PyObject *)self;
}
static int
ModPoly_init(ModPoly *self, PyObject *args, PyObject *kwds)
{
PyObject *r_modulus=NULL, *n_modulus=NULL, *coefs=NULL, *tmp;
PyObject **tmp_ar;
static char *kwlist[] = {"r_modulus", "n_modulus", "coefficients", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|O", kwlist,
&r_modulus, &n_modulus, &coefs))
return -1;
[...]
// The polynomial defaults to "x", so the coefficients should be [0, 1].
tmp_ar = (PyObject **)malloc(2 * sizeof(PyObject*));
if (tmp_ar == NULL) {
Py_DECREF(self->r_modulus);
Py_DECREF(self->n_modulus);
Py_DECREF(self->degree);
return -1;
}
tmp_ar[0] = PyInt_FromLong(0);
if (tmp_ar[0] != NULL) {
tmp_ar[1] = PyInt_FromLong(1);
}
if (tmp_ar[0] == NULL || tmp_ar[0] == NULL) {
Py_DECREF(self->r_modulus);
Py_DECREF(self->n_modulus);
Py_DECREF(self->degree);
Py_XDECREF(tmp_ar[0]);
Py_XDECREF(tmp_ar[1]);
free(tmp_ar);
return -1;
}
self->ob_size = 2;
self->allocated = 2;
return 0;
}
[...]
static PyObject *
ModPoly_getcoefs(ModPoly *self, void *closure)
{
printf("here"); // "here" is never printed
PyTupleObject *res=(PyTupleObject*)PyTuple_New(self->ob_size);
Py_ssize_t i;
PyObject *tmp;
if (res == NULL)
return NULL;
for (i=0; i < self->ob_size; i++) {
tmp = self->ob_item[i];
Py_INCREF(tmp);
PyTuple_SET_ITEM(res, i, tmp);
}
return (PyObject *)res;
}
static PyObject *
ModPoly_setcoefs(ModPoly *self, PyObject *value, void* closure)
{
PyErr_SetString(PyExc_AttributeError,
"Cannot set the coefficients of a polynomial.");
return NULL;
}
[...]
static PyGetSetDef ModPoly_getsetters[] = {
{"coefficients",
(getter)ModPoly_getcoefs, (setter)ModPoly_setcoefs,
"The polynomial coefficients.", NULL},
{NULL, 0, 0, NULL, NULL}
};
static PyTypeObject ModPolyType = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
[...]
ModPoly_members, /* tp_members */
ModPoly_getsetters, /* tp_getset */
0, /* tp_base */
[...]
};
[...]
edit
I tried to reimplement the getter instruction by instruction, and I understood what I wasn't doing. In the ModPoly_init function I create the tmp_ar where I store the coefficients, but I do not assign it to self->ob_item.
-facepalm-
You only seem to be assigning to ModPoly.ob_item in ModPoly_new() (setting it to NULL).
ModPoly_getcoefs() then dereferences the null pointer, which would give you your segfault. It looks like you intended to assign to ob_item in ModPoly_init(), but don't actually get around to doing so.

Categories