python C extensions - argument check in constructor - python

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

Related

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.

How to store a pointer in a custom embedded Python object

I have created a custom Python type in C as per the tutorial https://docs.python.org/2.7/extending/newtypes.html#the-basics. In my C I receive a pointer to a struct, I want to be able to get and set the values in the struct from Python without taking a copy of it. I.e.
a = myObject.x() # gets the x value in the struct.
or
myObject.x(255) # sets the x value in the struct.
However I cannot see how to store the pointer in the python object.
My current object definition is currently just the basic object implementation from the python website.
typedef struct {
PyObject_HEAD
myStruct *s;
} KeyObject;
static PyTypeObject KeyType = {
PyVarObject_HEAD_INIT(NULL, 0)
"ckb.Key", /* tp_name */
sizeof(KeyObject), /* 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 */
"Key objects", /* tp_doc */
};
static PyMethodDef key_methods[] = {
{NULL} /* Sentinel */
};
Here's an example (cbk.c), that can act as a backbone for this task:
#include "external.h"
#include "Python.h"
#define MOD_NAME "ckb"
#define KEY_CLASS_NAME "Key"
/*
typedef struct InnerStruct_tag {
int x;
} InnerStruct;
//*/
typedef struct KeyObject_tag {
PyObject_HEAD
InnerStruct *inner;
} KeyObject;
static PyObject *Key_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
KeyObject *self;
self = (KeyObject*)type->tp_alloc(type, 0);
if (self != NULL) {
//self->inner = (InnerStruct*)calloc(1, sizeof(Key));
self->inner = getExternalPtr(1234); // Don't allocate here, get the pointer from external lib
if (self->inner == NULL) {
Py_DECREF(self);
return NULL;
}
}
return (PyObject*)self;
}
static void Key_dealloc(KeyObject *self) {
//free(self->inner);
delExternalPtr(self->inner); // Use the external dellocation function (optional)
Py_TYPE(self)->tp_free((PyObject*)self);
}
static PyObject *Key_getX(KeyObject *self, void *closure) {
return PyInt_FromLong(self->inner->x);
}
static int Key_setX(KeyObject *self, PyObject *value, void *closure) {
if (value == NULL) {
PyErr_SetString(PyExc_TypeError, "Cannot delete 'x'");
return -1;
}
if (!PyInt_Check(value)) {
PyErr_SetString(PyExc_TypeError, "'x' value must be an int");
return -1;
}
self->inner->x = ((PyIntObject*)value)->ob_ival;
return 0;
}
static PyGetSetDef Key_getsets[] = {
{"x", (getter)Key_getX, (setter)Key_setX, "x", NULL},
{NULL} // Sentinel
};
static PyTypeObject Key_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
MOD_NAME"."KEY_CLASS_NAME, /* tp_name */
sizeof(KeyObject), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)Key_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 */
KEY_CLASS_NAME" objects", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
0, /* tp_members */
Key_getsets, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
Key_new, /* tp_new */
};
#define Key_CheckExact(op) ((op)->ob_type == &Key_Type)
static PyMethodDef module_methods[] = {
{NULL} // Sentinel
};
PyMODINIT_FUNC initckb(void) {
PyObject* m;
if (PyType_Ready(&Key_Type) < 0)
return;
m = Py_InitModule3(MOD_NAME, module_methods,
MOD_NAME": Example module that creates an extension type ("KEY_CLASS_NAME").");
Py_INCREF(&Key_Type);
PyModule_AddObject(m, KEY_CLASS_NAME, (PyObject*)&Key_Type);
}
Notes:
Existing structure names / members was renamed (reorganized), for clarity
The inner structure only has one member (x), which is enough for making a point
Everything relies solely on the [Python 2.7.Docs]: Defining New Types page (that was mentioned in the question as well)
Since the wrapper object (Key) contains pointers (inner), in order to avoid checking them for NULL every time they are accessed:
A constructor (Key_new - equivalent to __new__ in Python) - that initializes them - was added
A destructor (Key_dealloc - equivalent __del__ in Python) - that does the exact opposite - was added as well, to avoid memory leaks (this is just a previous bullet consequence)
Access to InnerStruct's x member is done via Key_getX, Key_setX functions (note they are referenced in Key_getsets):
From Python, the inner x member, will be accessed by key_instance.x, as it was a Key instance attribute
This way makes more sense than using a getter (get_x()) and a setter (set_x(value)), and is more Pythonic
If however the getter / setter way is preferred, Key_getX, Key_setX signatures should be slightly modified (I think removing the last argument would do), and they should be referenced in Key_methods - that should be specified to KeyType as tp_methods (also described in the above web page)
When adding new members to InnerStruct, only the stuff done for x needs to be replicated and adapted (of course if there will be functions that look too similar, code should be refactored - but this is outside current scope)
The last part is pretty standard Python extension module code
Update #0
After the 1st comment, it seems like the question is trickier than it seems. Not sure if I still get it wrong, because it doesn't seem such a big deal. The change is (as I understood), that the inner pointer should come from somewhere else (another library (.dll)), instead of being created in the constructor. Changed to the example to mimic the new (and hopefully expected) behavior:
Since a InnerStruct pointer is returned by the external library (called it external.dll), the structure definition was moved in a header file belonging to that library - called it external.h (below), which is included by cbk.c
Would make sense that a library exports some data via a function (also exported by the library): getExternalPtr which may take arguments - currently it only has (a dummy) one: dummyArg0
Since getExternalPtr allocates memory inside, would make sense to have a corresponding function that deallocates it (delExternalPtr), in order to avoid memory leaks and Undefined Behavior (e.g. if memory is allocated in one place, deallocated in another, and the 2 places are deserved by different C runtimes). Any pointer returned by getExternalPtr should be passed to delExternalPtr exactly once
The 2 above functions will now be called from Key_new and Key_dealloc. If this is still not OK, and the object needs to be modified after creation (although it may be possible that some race issues would arise), setting the member could be done like: ((KeyObject*)keyInstancePyObjectPtr)->inner = getExternalPtr(0); with just one catch:
keyInstancePyObjectPtr (which is a generic PyObject*) should be of type Key_Type. Key_CheckExact macro does exactly that check
Now, the module depends (is linked to) on the external lib (not sure how things actually are), but that can be changed to Dynamic (DLL (SO)) loading (via [Man7]: DLOPEN(3) / [Man7]: DLSYM(3) or [MSDN]: LoadLibrary function) / [MSDN]: GetProcAddress function
external library code:
external.h:
#if defined (WIN32)
# if defined (EXTERNAL_DYNAMIC)
# if defined EXTERNAL_EXPORTS
# define EXTERNAL_EXPORT __declspec(dllexport)
# else
# define EXTERNAL_EXPORT __declspec(dllimport)
# endif
# else
# define EXTERNAL_EXPORT
# endif
#else
# define EXTERNAL_EXPORT
#endif
typedef struct InnerStruct_tag {
int x;
} InnerStruct;
#if defined (__cplusplus)
extern "C" {
#endif
EXTERNAL_EXPORT InnerStruct *getExternalPtr(int dummyArg0);
EXTERNAL_EXPORT void delExternalPtr(InnerStruct *ptr);
#if defined (__cplusplus)
}
#endif
external.c:
#include "external.h"
#include <stdlib.h>
InnerStruct *getExternalPtr(int dummyArg0) {
InnerStruct *ret = (InnerStruct*)malloc(sizeof(InnerStruct));
if (ret != NULL)
ret->x = 1618;
return ret;
}
void delExternalPtr(InnerStruct *ptr) {
free(ptr);
}
Test program (ckb_test.py):
import traceback
import ckb
print "\nModule:", ckb
print "Dir:", dir(ckb)
print "\nClass:", ckb.Key
print "Dir:", dir(ckb.Key)
key = ckb.Key()
print "\nInstance:", key
print "Dir:", dir(key)
print "\nKey.x (initial):", key.x
key.x = 123
print "Key.x (modified):", key.x
try:
key.x = 1.0
except:
traceback.print_exc()
del(key)
print "\nEnd"
Output:
c:\Work\Dev\StackOverflow\q46833364>set PATH=%PATH%;.\external\Win32-Release
c:\Work\Dev\StackOverflow\q46833364>set PYTHONPATH=%PYTHONPATH%;.\ckb\Win32-Release
c:\Work\Dev\StackOverflow\q46833364\>"c:\Install\x86\HPE\OPSWpython\2.7.10__00\python.exe" ckb_test.py
Module: <module 'ckb' from 'c:\Work\Dev\StackOverflow\q46833364\ckb\Win32-Release\ckb.pyd'>
Dir: ['Key', '__doc__', '__file__', '__name__', '__package__']
Class: <type 'ckb.Key'>
Dir: ['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'x']
Instance: <ckb.Key object at 0x027A7050>
Dir: ['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'x']
Key.x (initial): 1618
Key.x (modified): 123
Traceback (most recent call last):
File "..\ckb_test.py", line 20, in <module>
key.x = 1.0
TypeError: 'x' value must be an int
End

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