PyInline Released: Put C source code directly "inline" with your Python!
Thomas Heller
thomas.heller at ion-tof.com
Wed Aug 29 03:00:41 EDT 2001
Inspired by the recent discussion of perls inline
module I wrote the attached module, which is yet
another way to do it.
Enjoy,
Thomas
----inline.py---
class C_Func:
def __init__(self, inline, name, code, doc, prefix):
self.name = name
self.code = code
self.doc = doc
self.module = inline
self.prefix = prefix
def create(self, file):
file.write("static PyObject *%s(PyObject *self, PyObject *args)\n"\
% (self.prefix + self.name))
file.write(self.code)
file.write("\n");
def __call__(self, *args, **kw):
mod = self.module.create()
func = getattr(mod, self.name)
return func(*args, **kw)
class Inline:
def __init__(self, name):
self.name = name
self.functions = {}
self.mod = None
self.libraries = []
def c_func(self, name, code, doc=None, prefix=''):
if self.mod:
raise RuntimeError, \
"Cannot add any more functions: Extension already created"
func = C_Func(self, name, code, doc, prefix)
self.functions[name] = func
return func
def create(self):
if not self.mod:
# generate the code
import StringIO
buffer = StringIO.StringIO()
buffer.write("#include <python.h>\n")
buffer.write("\n")
for func in self.functions.values():
func.create(buffer)
buffer.write("static PyMethodDef methods[] = {\n")
for fname in self.functions.keys():
pre = self.functions[fname].prefix
buffer.write(' { "%s", %s, METH_VARARGS },\n' % \
(fname, pre + fname))
buffer.write(' { NULL }, /* sentinel */\n};\n')
buffer.write("\n")
buffer.write('void init%s(void)\n' % self.name)
buffer.write('{\n Py_InitModule3("%s", methods, "");\n}\n' % \
self.name)
code = buffer.getvalue()
buffer.close()
# calulate md5 digest
import md5
m = md5.new()
m.update(code)
hd = m.hexdigest()
# compare to existing digest, if present
try:
old_md5 = open(self.name + '.md5', 'r').read()
except:
old_md5 = None
# if the digests match, no recompilation is needed
if old_md5 != hd:
# recompilation needed
file = open(self.name + '.c', 'w')
file.write(code)
file.close()
from distutils.core import setup, Extension
ext = Extension(self.name, sources=[self.name + '.c'],
libraries=self.libraries)
setup(ext_modules=[ext], script_name="dummy",
script_args=["-q", "install", "--install-platlib=."])
open(self.name + '.md5', 'w').write(hd)
import os
os.remove(self.name + '.c')
# import module
self.mod = __import__(self.name)
def __getattr__(self, name):
if name not in self.functions.keys():
raise AttributeError, name
if not self.mod:
self.create()
return getattr(self.mod, name)
if __name__ == '__main__':
module = Inline("_sample")
# define a function implemented in C
# The function declaration will be added automatically,
# in this case 'static PyObject *fib_c(PyObject *self, PyObject *args)'
module.c_func("fib_c", r"""
{
int i;
static int _fib(int);
if (!PyArg_ParseTuple(args, "i", &i))
return NULL;
return Py_BuildValue("i", _fib(i));
}
static int _fib(int i)
{
if (i <= 2)
return i;
return _fib(i - 1) + _fib(i - 2);
}
""")
# define a function implemented in C
# The function declaration will be added automatically,
# in this case 'static PyObject *fib_c(PyObject *self, PyObject *args)'
module.c_func("fact_c", r"""
{
int i;
PyObject *inst;
static int _fact(int);
if (!PyArg_ParseTuple(args, "Oi", &inst, &i))
return NULL;
return Py_BuildValue("i", _fact(i));
}
static int _fact(int i)
{
if (i <= 1)
return i;
return i * _fact(i - 1);
}
""")
# generate and compile an extension module (if needed),
# retrieve the function from it.
fib_c = module.fib_c
# a similar function implemented in Python
def fib_py(i):
if i <= 2:
return i
return fib_py(i-1) + fib_py(i-2)
# we can also define a class, and use the extension module's functions
# as instance methods
class X:
def fact_py(self, i):
if i <= 1:
return i
return i * self.fact_py(i-1)
# convert the extension function into a unbound method
# and attach it to the class
import new
X.fact_c = new.instancemethod(module.fact_c, None, X)
# do a little benchmark
import time
start = time.clock()
print "fib_py(30) =", fib_py(30), "%s seconds" % (time.clock() - start)
start = time.clock()
print "fib_c(30) =", fib_c(30), "%s seconds" % (time.clock() - start)
x = X()
start = time.clock()
print "x.fact_py(12) =", x.fact_py(12), "%s seconds" % (time.clock() - start)
start = time.clock()
print "x.fact_c(12) =", x.fact_c(12), "%s seconds" % (time.clock() - start)
More information about the Python-list
mailing list