473,414 Members | 1,677 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,414 software developers and data experts.

Wrapping a C library in Python

I've got a C library with about 50 calls in it that I want to wrap in
Python. I know I could use some tool like SWIG, but that will give me a
too-literal translation; I want to make some modifications along the way
to make the interface more Pythonic.

For example, all of these functions return an error code (typically just
errno passed along, but not always). They all accept as one of their
arguments a pointer to someplace to store their result. I want to
change all that to returning the result directly and throwing exceptions.

I also want to mutate some of the return types. A common way these
functions return a set of values is a pair of arrays of strings, forming
key-value pairs. In Python, it would make sense to return this as a
dictionary.

I know what I'm describing is kind of vague, but are there tools around
which might help automate (at least in part) this translation process?
Jul 18 '05 #1
13 3813

Take a close look at Pyrex at

<http://nz.cosc.canterbury.ac.nz/~greg/python/Pyrex/>
/Jean Brouwers
ProphICy Semiconductor, Inc.
In article <ro***********************@reader1.panix.com>, Roy Smith
<ro*@panix.com> wrote:
I've got a C library with about 50 calls in it that I want to wrap in
Python. I know I could use some tool like SWIG, but that will give me a
too-literal translation; I want to make some modifications along the way
to make the interface more Pythonic.

For example, all of these functions return an error code (typically just
errno passed along, but not always). They all accept as one of their
arguments a pointer to someplace to store their result. I want to
change all that to returning the result directly and throwing exceptions.

I also want to mutate some of the return types. A common way these
functions return a set of values is a pair of arrays of strings, forming
key-value pairs. In Python, it would make sense to return this as a
dictionary.

I know what I'm describing is kind of vague, but are there tools around
which might help automate (at least in part) this translation process?

Jul 18 '05 #2
[Roy Smith]
I've got a C library with about 50 calls in it that I want to wrap in
Python. I know I could use some tool like SWIG, but that will give me a
too-literal translation; I want to make some modifications along the way
to make the interface more Pythonic.


I used Pyrex with both pleasure and success for wrapping C libraries
while giving the API a more Pythonic flavour. I found Pyrex to be a
wonderful tool for easily doing such things.

--
François Pinard http://pinard.progiciels-bpi.ca
Jul 18 '05 #3
Roy Smith wrote:
I've got a C library with about 50 calls in it that I want to wrap in
Python.


I'd recommend ctypes (http://starship.python.net/crew/theller/ctypes/).
It is very easy to use and multi-platform.

I have a small project that provides a DB API 2.0 interface to the ODBTP
(http://odbtp.sf.net) library. See http://benjiyork.com/odbtp.html
(beware programmer-web-design ahead) for the code (LGPL). It might
serve as an example to get you on your way.

BTW, if anyone is interested, I hope to have a much improved version of
the wrapper ready in a few weeks to be included in the official ODBTP
distribution.
--
Benji York
be***@benjiyork.com

Jul 18 '05 #4
Roy Smith <ro*@panix.com> wrote in message news:<ro***********************@reader1.panix.com> ...
I've got a C library with about 50 calls in it that I want to wrap in
Python. I know I could use some tool like SWIG, but that will give me a
too-literal translation; I want to make some modifications along the way
to make the interface more Pythonic.

For example, all of these functions return an error code (typically just
errno passed along, but not always). They all accept as one of their
arguments a pointer to someplace to store their result. I want to
change all that to returning the result directly and throwing exceptions.

I also want to mutate some of the return types. A common way these
functions return a set of values is a pair of arrays of strings, forming
key-value pairs. In Python, it would make sense to return this as a
dictionary.

I know what I'm describing is kind of vague, but are there tools around
which might help automate (at least in part) this translation process?


While SWIG is a strong tool for wrapping C code into many different
langauges, there are definitely better tools out there for producing
python modules from C code.

I recommend both boost::python
(http://www.boost.org/libs/python/doc/index.html) and ctypes
(http://starship.python.net/crew/theller/ctypes/), but for very
different reasons.

If wrapped code speed and production of a binary is a goal in creating
your python extensions, use boost::python. The price you pay for
creating an fast binary python extension is coding your translation
from python in C/C++ in C/C++ (I suppose you could get around this by
creating a boost::python module, and then wrapping it with a python
module to do the type translation). And in boost::python, C++ to
python exception translation is supported.

If speed of development is your main goal, then I'd use ctypes.
ctypes will dynamically load the library in python code. At the
python code level, you should then do the translation to python types.
No C/C++ coding required.

Hope this helps!

Michael Loritsch
Jul 18 '05 #5
>>>>> "Michael" == Michael Loritsch <lo******@gmail.com> writes:

Roy> For example, all of these functions return an error code
Roy> (typically just errno passed along, but not always). They
Roy> all accept as one of their arguments a pointer to someplace
Roy> to store their result. I want to change all that to
Roy> returning the result directly and throwing exceptions.

Roy> I also want to mutate some of the return types. A common way
Roy> these functions return a set of values is a pair of arrays of
Roy> strings, forming key-value pairs. In Python, it would make
Roy> sense to return this as a dictionary.

SWIG can do all this - see the section on typemaps and call policies
in the SWIG manual.

For example, it is easy to tell SWIG that pointers are used for output

void somefunc(double *OUTPUT, double *OUTPUT)

will be called from python like

x, y = o.somefunc()

And you need to do no more than add the one declaration line. INPUT,
OUTPUT and INOUT are special tokens that SWIG recognizes and applies
translation rules too. You can define your own such tokens to do more
complicated things (like turning a double *array into a python list,
etc)

Michael> I recommend both boost::python
Michael> (http://www.boost.org/libs/python/doc/index.html) and
Michael> ctypes (http://starship.python.net/crew/theller/ctypes/),
Michael> but for very different reasons.

Michael> If wrapped code speed and production of a binary is a
Michael> goal in creating your python extensions, use
Michael> boost::python. The price you pay for creating an fast
Michael> binary python extension is coding your translation from
Michael> python in C/C++ in C/C++ (I suppose you could get around
Michael> this by creating a boost::python module, and then
Michael> wrapping it with a python module to do the type
Michael> translation). And in boost::python, C++ to python
Michael> exception translation is supported.

I don't fully agree with this. I've been working on a wrapper for
antigrain, a C++ library that makes heavy use of templates. I started
off using pyste (a boost::python generator) and boost. It worked
reasonably well - pyste is not being actively maintained right but you
can usually work around the limitations by writing boost code where
you need to. I was reasonably happy, until I saw my *.so files
ballooning. After wrapping a small fraction of the library, and
having instantiated only a few of the many templates I ultimately
wanted, my extension files were at 20MB, which is *much larger* than
the agg library or moderately sophisticated applications built around
it. And I still had *a lot* left to expose!

I started over in SWIG. First, SWIG has excellent support for C++ and
templates, and in many cases could auto-wrap and entire header with

%include "someheader.h"

Second, by the time I had the SWIG wrapping to a comparable point that
the boost wrapping was at when I started over, I had only a 500K
extension module. Same functionality, 40x smaller. Since ultimately
I may want to distribute my software in compiled form (eg a windows
installer) this is an important difference. Third, the compile times
were much shorter in SWIG. Fourth, SWIG produces c and cxx files as
its output, which you can distribute with your app (user doesn't need
to have SWIG to compile). This is not true for boost.

In a nutshell, for wrapping a large C++ library (not the original
poster's question, I know), I found SWIG more suitable for the reasons
above. I don't want to slam boost - I think it is an awesome package
-- but you should be aware of these potential problems. The ease of
wrapping agg was comparable in boost and SWIG.

Where boost (and pycxx) really shines above SWIG is when you want to
write functions and methods yourself that interact with python objects
-- for example if you were writing a python extension largely from
scratch rather than wrapping an existing library. The ability to use
friendly boost C++ classes like dict and list that manage memory for
you and have a pythonic feel is great.

JDH
Jul 18 '05 #6
John Hunter <jd******@ace.bsd.uchicago.edu> writes:
>> "Michael" == Michael Loritsch <lo******@gmail.com> writes:

Michael> I recommend both boost::python
Michael> (http://www.boost.org/libs/python/doc/index.html) and
Michael> ctypes (http://starship.python.net/crew/theller/ctypes/),
Michael> but for very different reasons.

Michael> If wrapped code speed and production of a binary is a
Michael> goal in creating your python extensions, use
Michael> boost::python. The price you pay for creating an fast
Michael> binary python extension is coding your translation from
Michael> python in C/C++ in C/C++ (I suppose you could get around
Michael> this by creating a boost::python module, and then
Michael> wrapping it with a python module to do the type
Michael> translation). And in boost::python, C++ to python
Michael> exception translation is supported.

I don't fully agree with this. I've been working on a wrapper for
antigrain, a C++ library that makes heavy use of templates. I started
off using pyste (a boost::python generator) and boost. It worked
reasonably well - pyste is not being actively maintained right but you
can usually work around the limitations by writing boost code where
you need to. I was reasonably happy, until I saw my *.so files
ballooning. After wrapping a small fraction of the library, and
having instantiated only a few of the many templates I ultimately
wanted, my extension files were at 20MB, which is *much larger* than
the agg library or moderately sophisticated applications built around
it. And I still had *a lot* left to expose!


Did you strip the extension modules (run 'strip' on the .so file)? I
know, there's nothing in distutils that will do that automatically. I
just did this on an extension module of mine using boost::python.
Before stripping, it was about 1.3 MB, afterwards, 50 kB.

The problem is the symbol table is kept by default, and it seems with
GNU C++ (at least) that can get *huge* when templates are involved.

Compile times are pain still.

--
|>|\/|<
/--------------------------------------------------------------------------\
|David M. Cooke
|cookedm(at)physics(dot)mcmaster(dot)ca
Jul 18 '05 #7
lo******@gmail.com (Michael Loritsch) wrote:
If speed of development is your main goal, then I'd use ctypes.
ctypes will dynamically load the library in python code. At the
python code level, you should then do the translation to python types.
No C/C++ coding required.


OK, I decided to give ctypes a try. After a few false steps (mostly
related to sorting out LD_LIBRARY_PATH issues), I got it to work for
simple functions. Once you get your head around how it works, it's
pretty neat.

The problem is, I'm at a loss what to do for a slightly more complex
case. The API I'm working with requires that you create a dm_handle
which you then pass into all the other calls to establish a context.
You start with (approximately):

/*
* Create a handle. The caller must have allocated memory for
* the object pointed to by dmh.
*/
create_dm_handle (struct dm_handle *dmh);

I don't see how I can do the required memory allocation. Calling
malloc() directly seems kind of scary. Even if I could do that, doing
sizeof (struct dm_handle) won't work in the Python environment.

Am I missing something obvious here?
Jul 18 '05 #8
Roy Smith wrote:
The problem is, I'm at a loss what to do for a slightly more complex
case.


The ctypes tutorial talks about this issue:
http://starship.python.net/crew/thel.../tutorial.html.
Especially the section s "Passing pointers" and "Structures and Unions".

I've also used the built-in struct module for simple cases.
--
Benji York
be***@benjiyork.com

Jul 18 '05 #9
Roy Smith <ro*@panix.com> writes:
lo******@gmail.com (Michael Loritsch) wrote:
If speed of development is your main goal, then I'd use ctypes.
ctypes will dynamically load the library in python code. At the
python code level, you should then do the translation to python types.
No C/C++ coding required.


OK, I decided to give ctypes a try. After a few false steps (mostly
related to sorting out LD_LIBRARY_PATH issues), I got it to work for
simple functions. Once you get your head around how it works, it's
pretty neat.

The problem is, I'm at a loss what to do for a slightly more complex
case. The API I'm working with requires that you create a dm_handle
which you then pass into all the other calls to establish a context.
You start with (approximately):

/*
* Create a handle. The caller must have allocated memory for
* the object pointed to by dmh.
*/
create_dm_handle (struct dm_handle *dmh);

I don't see how I can do the required memory allocation. Calling
malloc() directly seems kind of scary. Even if I could do that, doing
sizeof (struct dm_handle) won't work in the Python environment.

Am I missing something obvious here?


It's simple.
First, you define the structure:

import ctypes
class dm_handle(ctypes.Structure):
_fields_ = [....] # whatever is is

Then, create an instance (this will allocate memory internally)

my_handle = cm_handle()

and finally call the function, pssing it a pointer to the structure:

mydll.create_dm_handle(ctypes.byref(my_handle))

Thomas
Jul 18 '05 #10
In article <hd**********@python.net>,
Thomas Heller <th*****@python.net> wrote:
Am I missing something obvious here?


It's simple.
First, you define the structure:

import ctypes
class dm_handle(ctypes.Structure):
_fields_ = [....] # whatever is is

Then, create an instance (this will allocate memory internally)

my_handle = cm_handle()

and finally call the function, pssing it a pointer to the structure:

mydll.create_dm_handle(ctypes.byref(my_handle))


I guess that makes sense. In my case, however, the structure has about
20 elements, many of which are user-defined types. Building the correct
ctypes.Structure description would be a bit of work, and it would
certainly violate the rule of "once and only once". If the underlying C
structure changed, I'd have to update my Python code to match.

Since the handle is opaque, I don't need to know about the innards of
the structure at all. What I ended up doing was writing a little C
routine something like this:

dm_handle *allocate_dm_handle ()
{
return malloc (sizeof (struct dm_handle));
}

I then built a .so containing just that one routine, and used ctypes to
call it from Python to get my buffer.
Jul 18 '05 #11

"Roy Smith" <ro*@panix.com> wrote in message news:ro***********************@reader1.panix.com.. .
Since the handle is opaque, I don't need to know about the innards of
the structure at all. What I ended up doing was writing a little C
routine something like this:

dm_handle *allocate_dm_handle ()
{
return malloc (sizeof (struct dm_handle));
}

I then built a .so containing just that one routine, and used ctypes to
call it from Python to get my buffer.


Congralutions on just manually re-inventing Swig :-) Unless your
library is trivial (which this code indicates it is not), I would
recommend also spending a little time with Swig.

In many cases Swig can parse your header files and build the
correct wrapper. The huge advantage of Swig is that you can
use it to generate wrappers for a large number of languages.
That comes in really helpful if you will also need wrappers for
Java, TCL etc.

Roger
Jul 18 '05 #12
On Tue, 2004-11-23 at 00:33, Roger Binns wrote:
In many cases Swig can parse your header files and build the
correct wrapper. The huge advantage of Swig is that you can
use it to generate wrappers for a large number of languages.
That comes in really helpful if you will also need wrappers for
Java, TCL etc.


Just out of interest, do you know if SWIG can be used to generate an
extension module to provide interfaces to the API of an application that
embeds a Python interpreter?

(Still trying to get Py_NewInterpreter / Py_EndInterpreter to work in a
single-threaded app)

--
Craig Ringer

Jul 18 '05 #13
Hi Craig,
Just out of interest, do you know if SWIG can be used to generate an
extension module to provide interfaces to the API of an application that
embeds a Python interpreter?


Why not?

We do that for the next generation of an open source
finite-element-toolkit driven by an embedded Python interpreter.

And it was really nice to see how easy it is to retro-fit new SWIG
wrapped code to already existing hand written wrappers and externally
developed Python wrappers (vtk). So in fact we were able to dramatically
reduce the time spent for writing wrappers and adaptor code just by
using SWIG.

What we needed wouldn't have been possible with Boost.Python and it
would also mean a longer compile time.

Mark
Jul 18 '05 #14

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

6
by: USCode | last post by:
Does Python have facilities for wrapping up the interpreter, any necessary modules, GUI library, python scripts, image files, etc. up into a single executable like exists for Tcl/Tk? e.g....
0
by: Terry Hancock | last post by:
Hi all, Suppose you have found a really nice module that does what you want (or will do, it still being in development), and you don't want to waste a lot of duplicated effort, but this module...
1
by: TPJ | last post by:
I'm trying to get a wrapper for my C code in order to be able to use it as a module in Python. I'm doing it as follows: C code (file_c.c): ------------------------------- #include <stdio.h> ...
2
by: Jacob Cohen | last post by:
Under VC7.1, I am trying to wrap a native-C++ DLL that contains C++ objects in a Managed-C++ class library for use in a C# project. I created and compiled the native DLL under VC7.1 as a Win32...
10
by: Tom the Canuck | last post by:
What would be the best way to proceed? Should I make a pure virtual class and then derive from that? I want the base class to have functions defined so that I don't have to do the work all over...
3
by: gabriel.becedillas | last post by:
Hi, I'm having problems wrapping a hierarchy of classes, actually having problems wrapping the base class. I don't need to use the WrapClass mechanism since I don't want to override classes in...
0
by: Paul Anton Letnes | last post by:
>
8
by: Anish Chapagain | last post by:
Hi!! I tried wrapping a simple C code suing SWIG to Python, but am having problem, my .c file is, Step 1: example.c -------------- double val=3.0; int fact(int n)
5
by: Charlie | last post by:
Hi All, I am new to using swig/C++/python. I got some problem with function pointers. I posted in swig-user, but got no response. So I forwarded it here. You help is greatly appreciated. ...
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...
0
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.