3.2. Creating a Python Extension

PyMØD is primarily Python bindings for libMØD made using Boost.Python. The following shows a template for creating a Python extension with C++ functions (e.g., to extend libMØD).

3.2.1. Library Source and Headers

For this examples the following C++ source with header should be exposed in the Python module. Header (download):

#ifndef AWESOMELIB_H
#define AWESOMELIB_H

#include <mod/Graph.h>

namespace awesome {

std::shared_ptr<mod::Graph> doStuff();

} // namespace awesome

#endif // AWESOMELIB_H

Source (download):

#include "stuff.h"

namespace awesome {

std::shared_ptr<mod::Graph> doStuff() {
	auto g = mod::Graph::smiles("CCO");
	g->setName("Ethanol");
	return g;
}

} // namespace awesome

3.2.2. Exposing the Interface

See the Boost.Python documentation for instructions how to expose C++ code. Below is the code for creating our simple Python extension (download).

#include <boost/python.hpp>

#include "stuff.h"

#include <mod/Config.h>

namespace py = boost::python;

namespace {
	// this can be used to make sure the extension and mod is using the same shared library
	uintptr_t magicLibraryValue() {
		return (uintptr_t)&mod::getConfig();
	}
}

BOOST_PYTHON_MODULE(awesome) {
	py::def("magicLibraryValue", &magicLibraryValue);

	py::def("doStuff", &awesome::doStuff);
}

The example exposes a bit of extra functionality for a sanity check. Python will dlopen libMØD twice As both the PyMØD module and our extension requires it. The library uses static variables and strange things might happen if multiple instances of these exist. The MØD wrapper script changes the dlopen flags (setting RTLD_GLOBAL and RTLD_NOW) which should prevent multiple instances of the library. The function magicLibraryValue can be used to check that this is true (see the test script below).

3.2.3. Makefile

A Makefile fragment to build the example (download):

export PKG_CONFIG_PATH := ${PKG_CONFIG_PATH}:<mod installation path>/lib/pkgconfig
CPPFLAGS                = $$(pkg-config --cflags mod)
CPPFLAGS               += -I<Boost installation path>/include
CPPFLAGS               += $$(python3-config --includes)
CXXFLAGS                = -std=c++11 -fpic
LDFLAGS                 = -Wl,--no-undefined -L<Boost installation path>/lib
LDLIBS                  = $$(pkg-config --libs mod) -lboost_python3
LDLIBS                 += $$(python3-config --libs)

OBJECTS                 = pyModule.o stuff.o

awesome.so: $(OBJECTS)
	$(CXX) -shared $(LDFLAGS) -o awesome.so $(OBJECTS) $(LDLIBS)

clean:
	rm -f awesome.so $(OBJECTS)

3.2.4. Testing the Extension

Using the Makefile above creates the shared library awesome.so which contains the extension. Executing the following script using the wrapper script in the same folder as the shared libray should now work (download):

import awesome

# sanity check for multiple copies of libMØD
modValue = mod.magicLibraryValue()
ourValue = awesome.magicLibraryValue()
if modValue != ourValue:
	print("mod =", modValue)
	print("our =", ourValue)
	raise Exception("Magic values differ! I.e., more than one instance of libMØD has been loaded.")
# end if check

g = awesome.doStuff()
print("Got a graph:", g.name)
g.print()

Command to execute:

mod -f test.py