AttributeError: 'module' object has no attribute 'func' Only With C ++ Invocation

I have been working on integrating the scripting engine into an existing architecture and used the boost.python library.

My goal is to create a python environment that I create to be identical or close to what is expected in existing code, i.e. preserving namespace conventions etc. The trick here is that I have two source files illustrating which works if I run the translator offline, but not in my environment, that is, I must be doing something wrong.

As a disclaimer I am not an experienced python user, so it is my responsibility to lure the correct terminology here.

core.py

import sys

def func():    
    print "sys is",sys

print "call from core"  
func()

      

main.py

import core

print "---------GLOBALS-------------"
globals_ = dict(globals())
for g in globals_:
    print g

print "--------------LOCALS---------"   
locals_ = dict(locals())
for l in locals_:
    print l

print "call from main"
core.func()

      

Calling python main.py

from the command line

call from core
sys is <module 'sys' (built-in)>
---------GLOBALS-------------
core
__builtins__
__name__
__file__
__doc__
__package__
--------------LOCALS---------
core
g
__builtins__
__file__
globals_
__package__
__name__
__doc__
call from main
sys is <module 'sys' (built-in)>

      

I understand the steps as:

  • main calls imports to import core.py.
  • The main module is loaded; the main namespace and the attributes it provides are made available from the main. In this process, core calls its func method, printing what is associated with sys

    .
  • main calls the function associated with the core of the module.

An example of how I start the scripting environment:

void Run()
{
    Py_Initialize();
    if(PyErr_Occurred())
    {
        PyErr_Print();
    }

    // Install builtins in __main__
    boost::python::object main_module = boost::python::import(ToplevelScope.c_str());
    boost::python::object main_namespace = main_module.attr("__dict__");
    boost::python::dict main_dictionary = boost::python::extract<boost::python::dict>(main_namespace);
    main_dictionary["__builtins__"] =  boost::python::handle<>(boost::python::borrowed(PyEval_GetBuiltins()));

    // Load modules
    LoadModule("core.py",GetDataForCore());
    LoadModule("main.py",GetDataForMain());
}

void LoadModule(std::string name, char* data)
{
    // First, see if the module already exists.
    PyObject* new_module;
    if( new_module = PyImport_AddModule(name.c_str()) )
    {
        if( PyDict_GetItemString(new_module,"__name__") )
        {
            return; // already loaded. no work to do.
        }
    }

    // Initialize default namespace parameters - global, local
    boost::python::dict base_dict = boost::python::dict();
    base_dict["__builtins__"] = boost::python::handle<>(PyImport_AddModule("__builtin__"));
    base_dict["__name__"] = name.c_str();
    base_dict["__file__"] = name.c_str();
    base_dict["__doc__"] = "None";
    base_dict["__package__"] = name.c_str();

    boost::python::exec(    data,
                            boost::python::dict(base_dict),  // make a copy of base_dict for both global & local
                            boost::python::dict(base_dict) );
}

      

Resulting output:

call from core
sys is <module 'sys' (built-in)>
---------GLOBALS-------------
core
__builtins__
__name__
__file__
__doc__
__package__
--------------LOCALS---------
core
g
__builtins__
__file__
globals_
__package__
__name__
__doc__
call from main
[LOG] Framework error while loading script main.py.
Traceback (most recent call last):
  File "<string>", line 17, in <module>
AttributeError: 'module' object has no attribute 'func'

      

It looks like the function in the kernel is somehow not available to the main one even after import.

I suspect this is because I haven't fully initialized the main module. Replacing exec with PyRun_String, PyImport_ImportModule ... and they all give the same result. Here I am making a copy of the base dictionary with the name set in the module name (in this case the kernel) and supplying it as global and local dictionaries.

I think I am not fully initializing the module somehow and will be doing a lot more reading to build it ... Any insight would be greatly appreciated !!

+3


source to share


1 answer


You seem to enjoy reinventing the wheel. boost::python::import

does exactly what you want to do. Loads a module or retrieves it from sys.modules

if it was already imported earlier. If you don't want to use boost, you can use PyObject* PyImport_ImportModule(const char *name)

. Just make sure the module is in the python path.

However, if the module does not exist as a file (say the source only exists in memory), then the following recreates this functionality (it does not do any error checking, you need to add that yourself).



py::object LoadModule(std::string name, const char* module_source)
{
    namespace py = boost::python;

    // create module name and filenames
    py::object name_str(py::str(name.c_str()));
    std::string file = name;
    file += ".py";
    py::object file_str(py::str(file.c_str()));

    // get helper modules
    py::object types(py::import("types"));
    py::object sys(py::import("sys"));

    if (py::dict(sys.attr("modules")).has_key(name_str)) {
        return sys.attr("modules")[name_str];
    }

    // create empty module
    py::object module(types.attr("ModuleType")(name_str));

    // init module dict
    py::dict base_dict = py::extract<py::dict>(module.attr("__dict__"));
    base_dict["__builtins__"] = py::import("__builtins__");
    base_dict["__name__"] = name_str;
    base_dict["__file__"] = file_str;

    // execute module code in module context
    py::exec(module_source, base_dict, base_dict);

    // add module to sys.modules
    sys.attr("modules")[name] = module;

    return module;
}

      

+1


source







All Articles