From: Thomas H. <tho...@io...> - 2001-08-29 07:11:28
|
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) |