Python interface to plugins

Hello developers,

I wanted to ask a question concerning a topic that might be of general interest to developers of plugins. I am trying to write a psi4 plugin with a python interface, that is, a python module that make classes and functions in my plugin available in a psi4 input file. To start I created a new plugin called py_interface. Then, I tried to implement a simple function that returns a char* and link it to python:

#include <boost/python.hpp>
#include <boost/python/dict.hpp>
#include <boost/python/tuple.hpp>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>

#include "forte.h"

using namespace boost;
using namespace boost::python;
using namespace psi;

namespace psi{ namespace py_interface {

extern "C"
int read_options(std::string name, Options& options)
{
    if (name == "PY_INTERFACE"|| options.read_globals()) {
        /*- The amount of information printed to the output file -*/
        options.add_int("PRINT", 1);
    }

    return true;
}

char const* greet()
{
   return "hello, world";
}

BOOST_PYTHON_MODULE (py_interface) // Attempt #1: create module (possible name conflict?)
//void export_py_interface()  // Attempt #2: export interface
{
    def("greet", greet);
}

extern "C"
SharedWavefunction py_interface(SharedWavefunction ref_wfn, Options& options)
{
    int print = options.get_int("PRINT");

    // void export_py_interface() // Attempt #2: export interface
    return ref_wfn;
}

}} // End namespaces

The following call in a psi4 input fails:

print py_interface.greet()
    >>> An error has occurred. Traceback:
    >>> <type 'exceptions.AttributeError'>: 'module' object has no attribute 'greet':   File "<string>", line 32, in <module>

In my second attempt, I exported directly the interface without creating a module (see comments in above code). In this case the input

energy('py_interface')

print py_interface.greet()

fails during the call to export_py_interface() in the function py_interface:

An error has occurred. Traceback:
<type 'exceptions.AttributeError'>: 'NoneType' object has no attribute '__dict__':   File "<string>", line 29, in <module>

  File "/Users/francesco/Source/psi4/share/python/driver.py", line 570, in energy
    wfn = procedures['energy'][lowername](lowername, molecule=molecule, **kwargs)

  File "./../py_interface/pymodule.py", line 66, in run_py_interface
    py_interface_wfn = psi4.plugin('py_interface.so', ref_wfn)

This leads me to wonder if it is actually possible at all to export plugins functions as python functions.

I would greatly appreciate any suggestion or solution.

I’ve not dealt with boost python modules outside of the psi4 boost python module. It may help disentangling things to decouple the py_interface plugin name (that you know will import py_interface beacause of the plugin’s __init__.py) with the boost python module py_interface (that if it imported in pymodule.py would be a significant victory). And try doing testing from pymodule.py rather than an input file to avoid input mangling oddities.

If Python was loading your plugin file then what you are attempting would work. However, Psi4 is loading your plugin and not Python. In the current system either the Python bits or the Psi4 bits would work, not both. If this is even possible to do someone would have to modify our plugin loading mechanism to register your changes to the Python module.

In the envisioned post-1.0 psi4 where psi4 is routinely compiled to psi4.so and loaded into generic python as a module import psi4 that loads the driver that in turn imports psi4.so the boost python module, I think what you’re trying to do should be easier.

@loriab and @jturney: thanks for the feedback. I guess this should be a feature request for post-1.0 psi4.