Wrapping openbabel in python - using cython
The previous post showed a contrived example of how one could access the openbabel functionality in python using the boost libraries. There is alternative to boost to wrap around c/c++ code, it's called cython. Here is an example openbabel wrapper, exposing some very simple functionality.
Just before diving in, you should know that there already exists a wrapper around openbabel in python (it is called pybel). What I'm showing here doesn't even come close to pybel in terms of usefulness, the idea here is to show a demo of how easy wrapping things in python is.
#! /usr/bin/python from ez_setup import use_setuptools use_setuptools() from setuptools import setup, Extension, find_packages from Cython.Distutils import build_ext import sys, os import glob import subprocess as sub def get_include(name="openbabel"): p = sub.Popen('locate %s' % name ,stdout=sub.PIPE,stderr=sub.PIPE, shell=True) output, errors = p.communicate() include = [str.split("%s%s%s" %(os.sep, name, os.sep))[0] for str in output.split("\n") if str.endswith(".h")] include = set(include) assert len(include) == 1 return list(include)[0] def main(): extensions = [ Extension('_pyopenbabel', glob.glob('src/*.pyx'), [get_include('openbabel')], language="c++",libraries=['openbabel']) ,] setup(name = 'pyopenbabel', ext_package = 'pyopenbabel', cmdclass = {'build_ext': build_ext}, ext_modules = extensions, packages = find_packages() ) if __name__ == '__main__': main()The actual wrapper is in src/openbabel.pxd. In it you register which things from the openbabel headers you're going to use/wrap. Note that all the "cdef extern from openbabel/..." statements will fail if the inculudes defined in setup.py are not actually pointing to a place where a openbabel headers live. If you want to find out what a header file is, or how to find it - for openbabel or any other piece of code - google will provide a fast soultion.
# distutils: language = c++ from libcpp.string cimport string from libcpp cimport bool cdef extern from "openbabel/base.h" namespace "OpenBabel": cdef cppclass OBBase: pass cdef extern from "openbabel/mol.h" namespace "OpenBabel" : cdef cppclass OBMol(OBBase): OBMol() except + const char *GetTitle(bool replaceNewlines = true) unsigned int NumAtoms() OBAtom *GetAtom(int idx) cdef extern from "openbabel/atom.h" namespace "OpenBabel": cdef cppclass OBAtom(OBBase): double GetX() double GetY() double GetZ() cdef extern from "openbabel/obconversion.h" namespace "OpenBabel" : cdef cppclass OBConversion: OBConversion() except + bool SetInAndOutFormats(const char* inID, const char* outID) bool SetInFormat(const char* inID) bool ReadString(OBBase* pOb, string input) bool ReadFile(OBBase* pOb, string filePath)We register it's use in src/pyopenbabel.pyx Finally, in pyopenbabel/__init__.py put
from _pyopenbabel import *This will magically import everything from the wrapper (registered as cython extension in setup.py). Why do I think this is important? Because it's fast, easy and may allow interesting chemistry to happen.
Comments
Post a Comment