'How to call a python function from C++ with pybind11?

Please consider the following C++ pybind11 program:

#include <pybind11/embed.h>

namespace py = pybind11;

int main() {
    py::scoped_interpreter guard{};

    py::dict locals;

    py::exec(R"(

        import sys

        def f():
            print(sys.version)

    )", py::globals(), locals);

    locals["f"]();  // <-- ERROR
}

The py::exec call and the enclosed import sys call both succeed, but the call locals["f"]() throws an exception:

NameError: name 'sys' is not defined

on the first line of function f.

Expected behaviour is that the program prints the python system version.

Any ideas?

Update:

I modified the program as suggested by @DavidW:

#include <pybind11/embed.h>

namespace py = pybind11;

int main() {
    py::scoped_interpreter guard{};

    py::dict globals = py::globals();

    py::exec(R"(

        import sys

        def f():
            print(sys.version)

    )", globals, globals);

    globals["f"]();  // <-- WORKS NOW
}

and it now works.

I'm not 100% sure I understand what is going on, so I would appreciate an explanation.

(In particular does the modification of the common globals / locals dictionary impact any other scripts. Is there some global dictionary that is part of the python interpreter that the exec script is modifying? or does py::globals() take a copy of that state so the execed script is isolated from other scripts?)

Update 2:

So it looks like having globals and locals be the same dictionary is the default state:

$ python
>>> globals() == locals()
True
>>> from __main__ import __dict__ as x
>>> x == globals()
True
>>> x == locals()
True

...and that the default value for the two is __main__.__dict__, whatever that is (__main__.__dict__ is the dictionary returned by py::globals())

I'm still not clear what exactly __main__.__dict__ is.



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source