|
From: Ken S. <ksi...@tt...> - 2001-08-29 18:34:54
|
> Inspired by the recent discussion of perls inline
> module I wrote the attached module, which is yet
> another way to do it.
Thanks for posting your solution, Thomas. I have added another example
(example4.py) which steals your Fibonacci function and demonstrates how
you can add Inlined C methods to a Python class.
I also just added (but have not yet committed into CVS) a build() method
to the PyInline class, which simplifies and abstracts the build process.
e.g.,
import PyInline, __main__
PyInline.build(code=r"""
#include <stdio.h>
void ja(char *str) {
printf("Just another %s hacker\n", str);
}
""",
targetmodule=__main__,
language = 'C')
ja("Inline")
TTUL
Ken
> 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)
>
>
>
>
>
>
> _______________________________________________
> Pyinline-discuss mailing list
> Pyi...@li...
> http://lists.sourceforge.net/lists/listinfo/pyinline-discuss
>
|