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