Python : retrieve variable/result from C module - python

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()

Related

kernel module - read buffer from the my module with python by ioctl or sysfs

I built a kernel module that uses ioctl to communicate with a program in python in user space. The program in python should use ioctl to get a string from the kernel module and then print it on the screen.
I do not understand how to use what python in ioctl as we talk in the example at the following link:
enter link description here
In addition it is important to note that I use ubuntu 22.04 in kernel5.15.
My module is:
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include<linux/slab.h>
//kmalloc()
#include<linux/uaccess.h>
//copy_to/from_user()
#include <linux/ioctl.h>
#define WR_VALUE _IOW('a','a',int32_t*)
#define RD_VALUE _IOR('a','b',char*)
int32_t value = 0;
dev_t dev = 0;
static struct class *dev_class;
static struct cdev etx_cdev;
/*
** Function Prototypes
*/
static int __init etx_driver_init(void);
static void __exit etx_driver_exit(void);
static int etx_open(struct inode *inode,
struct file *file);
static int etx_release(struct inode
*inode, struct file *file);
static ssize_t etx_read(struct file *filp,
char __user *buf, size_t len,loff_t * off);
static ssize_t etx_write(struct file *filp,
const char *buf, size_t len, loff_t * off);
static long etx_ioctl(struct file *file,
unsigned int cmd, unsigned long arg);
/*
** File operation sturcture
*/
static struct file_operations fops =
{
.owner = THIS_MODULE,
.read = etx_read,
.write = etx_write,
.open = etx_open,
.unlocked_ioctl = etx_ioctl,
.release = etx_release,
};
/*
** This function will be called when we open
the Device file
*/
static int etx_open(struct inode *inode, struct
file *file)
{
pr_info("Device File Opened...!!!\n");
return 0;
}
/*
** This function will be called when we close
the Device file
*/
static int etx_release(struct inode *inode,
struct file *file)
{
pr_info("Device File Closed...!!!\n");
return 0;
}
/*
** This function will be called when we the
Device file
*/
char array[40] = "hello world hhhhhhhhhhhhhh";
static ssize_t etx_read(struct file *filp, char
__user *buf, size_t len, loff_t *off)
{
pr_info("Read Function\n");
return 0;
}
/*
** This function will be called when we
write the Device file
*/
static ssize_t etx_write(struct file *filp,
const char __user *buf, size_t len, loff_t
*off)
{
pr_info("Write function\n");
return len;
}
/*
** This function will be called when we write
IOCTL on the Device file
*/
static long etx_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
switch(cmd) {
case WR_VALUE:
if( copy_from_user(&value ,(int32_t*) arg, sizeof(value)) )
{
pr_err("Data Write : Err!\n");
}
pr_info("Value = %d\n", value);
break;
case RD_VALUE:
if( copy_to_user((char*) arg, array, 30) )
{
pr_err("Data Read : Err!\n");
}
break;
default:
pr_info("Default\n");
break;
}
return 0;
}
/*
** Module Init function
*/
static int __init etx_driver_init(void)
{
/*Allocating Major number*/
if((alloc_chrdev_region(&dev, 0, 1, "etx_Dev")) <0){
pr_err("Cannot allocate major number\n");
return -1;
}
pr_info("Major = %d Minor = %d \n",MAJOR(dev), MINOR(dev));
/*Creating cdev structure*/
cdev_init(&etx_cdev,&fops);
/*Adding character device to the system*/
if((cdev_add(&etx_cdev,dev,1)) < 0){
pr_err("Cannot add the device to the system\n");
goto r_class;
}
/*Creating struct class*/
if((dev_class = class_create(THIS_MODULE,"etx_class")) == NULL){
pr_err("Cannot create the struct class\n");
goto r_class;
}
/*Creating device*/
if((device_create(dev_class,NULL,dev,NULL,"etx_device")) == NULL){
pr_err("Cannot create the Device 1\n");
goto r_device;
}
pr_info("Device Driver Insert...Done!!!\n");
return 0;
r_device:
class_destroy(dev_class);
r_class:
unregister_chrdev_region(dev,1);
return -1;
}
/*
** Module exit function
*/
static void __exit etx_driver_exit(void)
{
device_destroy(dev_class,dev);
class_destroy(dev_class);
cdev_del(&etx_cdev);
unregister_chrdev_region(dev, 1);
pr_info("Device Driver
Remove...Done!!!\n");
}
module_init(etx_driver_init);
module_exit(etx_driver_exit);
MODULE_LICENSE("GPL");
If there is a better way to do it using sysfs I would love to know.

Creating a module subclass in a Python extension

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;
}

Python object not fully initialized using C API

In the following scenario, the object is not meant to be instantiated within Python (hence no tp_new or tp_init). Calling the ThingType as a function using PyObject_CallObject results in a segfault in _PyObject_FastCallDict, I believe because there are no constructors.
After creating the object using CreatePythonThing, the function get_height is not set unless a workaround is applied - to call dir(thing), which as a side effect initializes the object's properties. The workaround is already present in CreatePythonThing.
#include <Python.h>
typedef struct {
PyObject_HEAD
/* Type-specific fields go here. */
Eval *eval;
} Thing;
static PyObject* ThingGetHeight(PyObject* self, PyObject* args)
{
return PyLong_FromLong(1);
}
static PyMethodDef ThingMethods[] = {
{"get_height", ThingGetHeight, METH_NOARGS, "Get thing height"},
{NULL, NULL, 0, NULL} /* Sentinel */
};
static PyTypeObject ThingType = {
PyVarObject_HEAD_INIT(NULL, 0)
"thing.Thing", /* tp_name */
sizeof(Thing), /* tp_basicsize */
0, /* tp_itemsize */
0, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
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 */
"Thing", /* tp_doc */
0, 0, 0, 0, 0, 0,
ThingMethods, /* tp_methods */
};
Thing* CreatePythonThing(Eval *eval)
{
Thing* obj = PyObject_New(Thing, &ThingType);
obj->eval = eval;
obj = (Thing*) PyObject_Init((PyObject*) obj, &ThingType);
// I don't understand why, but the above does not fully initialize the object. The method
// table is not set. Calling `dir` on the object causes it to be initialized, so this is
// a workaround.
PyObject_Dir((PyObject*) obj);
return obj;
}
As in DavidW's comment, I was missing PyType_Ready(&ThingType); in my global initializer.

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 instance method in C

Consider the following Python (3.x) code:
class Foo(object):
def bar(self):
pass
foo = Foo()
How to write the same functionality in C?
I mean, how do I create an object with a method in C? And then create an instance from it?
Edit:
Oh, sorry! I meant the same functionality via Python C API. How to create a Python method via its C API?
Something like:
PyObject *Foo = ?????;
PyMethod??? *bar = ????;
Here's a simple class (adapted from http://nedbatchelder.com/text/whirlext.html for 3.x):
#include "Python.h"
#include "structmember.h"
// The CountDict type.
typedef struct {
PyObject_HEAD
PyObject * dict;
int count;
} CountDict;
static int
CountDict_init(CountDict *self, PyObject *args, PyObject *kwds)
{
self->dict = PyDict_New();
self->count = 0;
return 0;
}
static void
CountDict_dealloc(CountDict *self)
{
Py_XDECREF(self->dict);
self->ob_type->tp_free((PyObject*)self);
}
static PyObject *
CountDict_set(CountDict *self, PyObject *args)
{
const char *key;
PyObject *value;
if (!PyArg_ParseTuple(args, "sO:set", &key, &value)) {
return NULL;
}
if (PyDict_SetItemString(self->dict, key, value) < 0) {
return NULL;
}
self->count++;
return Py_BuildValue("i", self->count);
}
static PyMemberDef
CountDict_members[] = {
{ "dict", T_OBJECT, offsetof(CountDict, dict), 0,
"The dictionary of values collected so far." },
{ "count", T_INT, offsetof(CountDict, count), 0,
"The number of times set() has been called." },
{ NULL }
};
static PyMethodDef
CountDict_methods[] = {
{ "set", (PyCFunction) CountDict_set, METH_VARARGS,
"Set a key and increment the count." },
// typically there would be more here...
{ NULL }
};
static PyTypeObject
CountDictType = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
"CountDict", /* tp_name */
sizeof(CountDict), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)CountDict_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 | Py_TPFLAGS_BASETYPE, /* tp_flags*/
"CountDict object", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
CountDict_methods, /* tp_methods */
CountDict_members, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)CountDict_init, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
};
// Module definition
static PyModuleDef
moduledef = {
PyModuleDef_HEAD_INIT,
"countdict",
MODULE_DOC,
-1,
NULL, /* methods */
NULL,
NULL, /* traverse */
NULL, /* clear */
NULL
};
PyObject *
PyInit_countdict(void)
{
PyObject * mod = PyModule_Create(&moduledef);
if (mod == NULL) {
return NULL;
}
CountDictType.tp_new = PyType_GenericNew;
if (PyType_Ready(&CountDictType) < 0) {
Py_DECREF(mod);
return NULL;
}
Py_INCREF(&CountDictType);
PyModule_AddObject(mod, "CountDict", (PyObject *)&CountDictType);
return mod;
}
You can't! C does not have "classes", it only has structs. And a struct cannot have code (methods or functions).
You can, however, fake it with function pointers:
/* struct object has 1 member, namely a pointer to a function */
struct object {
int (*class)(void);
};
/* create a variable of type `struct object` and call it `new` */
struct object new;
/* make its `class` member point to the `rand()` function */
new.class = rand;
/* now call the "object method" */
new.class();
I suggest you start from the example source code here -- it's part of Python 3's sources, and it exists specifically to show you, by example, how to perform what you require (and a few other things besides) -- use the C API to create a module, make a new type in that module, endow that type with methods and attributes. That's basically the first part of the source, culminating in the definition of Xxo_Type -- then you get examples of how to define various kinds of functions, some other types you may not care about, and finally the module object proper and its initialization (you can skip most of that of course, though not the module object and the parts of its initialization that lead up to the definition of the type of interest;-).
Most of the questions you might have while studying and adapting that source to your specific needs have good answers in the docs, especially in the section on "Object Implementation Support" -- but of course you can always open a new question here (one per issue would be best -- a "question" with many actual questions is always a bother!-) showing exactly what you're doing, what you were expecting as a result, and what you are seeing instead -- and you'll get answers which tend to include some pretty useful ones;-).

Categories