Python/C++ : Embedding script in Qt/C++ application Error - python

I am attempting to embed a python script in a Qt/C++ project. In my Qt Project file, I have added the Python Framework and the Python library location.
INCLUDEPATH += "/Library/Frameworks/Python.framework/Versions/3.6/lib"
INCLUDEPATH += "/Library/Frameworks/Python.framework/Versions/3.6/include/python3.6m"
LIBS += -F"/Library/Frameworks/Python.framework/" -framework python
After reading the documentation, I am able to run a simple python script, using PyRun_SimpleFile(). I am now trying to run a script that takes an argument as input from C++ and returns an argument as output.
Using the documentation, I could write the following function :
const char *ss = "MainFile";
PyObject* name ;
name = PyUnicode_DecodeFSDefault(ss);
PyObject* pluginModule = PyImport_ImportModule("MainFile");
Py_DECREF(name);
if (!pluginModule)
{
PyErr_Print();
return "Error importing module";
}
PyObject* filterFunc = PyObject_GetAttrString(pluginModule, "attoMainFunction");
Py_DECREF(pluginModule);
if (!filterFunc)
{
PyErr_Print();
return "Error retrieving 'attoMainFunction'";
}
PyObject* args = Py_BuildValue("(s)", s.toStdString().c_str());
if (!args)
{
PyErr_Print();
Py_DECREF(filterFunc);
return "Error building args tuple";
}
PyObject* resultObj = PyObject_CallObject(filterFunc, args);
Py_DECREF(filterFunc);
Py_DECREF(args);
if (!resultObj)
{
PyErr_Print();
return "Error invoking 'filterFunc'";
}
const char* resultStr = PyUnicode_DecodeFSDefault(resultObj);
if (!resultStr)
{
PyErr_Print();
Py_DECREF(resultObj);
return "Error converting result to C string";
}
std::string result = resultStr;
Py_DECREF(resultObj);
return result;
When I run the above function, I get the following error :
Undefined symbols for architecture x86_64:
"_PyUnicode_DecodeFSDefault", referenced from:
MainWindow::pythonTry(QString&) in mainwindow.o
Am I missing linking a Python library with the project? Or is there anything wrong with the function I'm calling?
Thanks for your time.

Related

Embedded Python still looking for debug symbols even with _DEBUG undefined

I'm trying to embed Python in a C++ application, and I need it to run as a release build, as I am only interested in debugging the C++ code. Also, I do not have the _d debug versions of all libraries I need. I am using Python 3.7.0, in MSVC 2017 with C++11, the library I don't have debug symbols for is VTK, which I installed through a wheel file supplied by my employer. If I try to build the Python wrapper myself I get another load of issues, so I am unable to build the debug files. If I run in Debug mode, I am unable to import the library, whereas if I run in Release, I get further issues:
Exception thrown at 0x00007FF90841DBC9 (python37_d.dll) in PythonEmbedding.exe: 0xC0000005: Access violation reading location 0x0000000000000025.
The C++ code:
int main(int argc, char *argv[])
{
PyObject *pIntrospector = NULL;
if (PyVtk_InitIntrospector(pIntrospector) == OK)
{
printf("Initialization succeeded\n");
}
vtkObjectBase *pConeSource = NULL;
if (PyVtk_CreateVtkObject(pIntrospector, "vtkConeSource", pConeSource) == OK)
{
printf("Object creation succeeded\n");
}
return 0;
}
int PyVtk_InitIntrospector(
PyObject *pIntrospector)
{
/* Activating virtual environment */
#ifdef _DEBUG
// For Visual Studio debug builds
const wchar_t *sPyHome = L"venv-dbg";
#else
// For release builds
const wchar_t *sPyHome = L"venv";
#endif
Py_SetPythonHome(sPyHome);
/* Initializing Python environment and setting PYTHONPATH. */
Py_Initialize();
PyRun_SimpleString("import sys\nimport os");
PyRun_SimpleString("sys.path.append( os.path.dirname(os.getcwd()) )");
PyRun_SimpleString("sys.path.append(\".\")");
PyRun_SimpleString("import importlib.machinery as m");
PyRun_SimpleString("print(m.all_suffixes())");
/* Decode module from its name. Returns error if the name is not decodable. */
PyObject *pIntrospectorModuleName = PyUnicode_DecodeFSDefault("Introspector");
if (pIntrospectorModuleName == NULL)
{
fprintf(stderr, "Fatal error: cannot decode module name\n");
return PYTHON_INTROSPECTION_STRING_DECODE_ERROR;
}
/* Imports the module previously decoded. Returns error if the module is not found. */
PyObject *pIntrospectorModule = PyImport_Import(pIntrospectorModuleName);
if (pIntrospectorModule == NULL)
{
if (PyErr_Occurred())
{
PyErr_Print();
}
fprintf(stderr, "Failed to load \"Introspector\"\n");
Py_DECREF(pIntrospectorModuleName);
return PYTHON_INTROSPECTION_MODULE_LOAD_ERROR;
}
/* Looks for the Introspector class in the module. If it does not find it, returns and error. */
PyObject* pIntrospectorClass = PyObject_GetAttrString(pIntrospectorModule, "Introspector");
if (pIntrospectorClass == NULL || !PyCallable_Check(pIntrospectorClass))
{
if (PyErr_Occurred())
{
PyErr_Print();
}
fprintf(stderr, "Cannot find class \"Introspector\"\n");
if (pIntrospectorClass != NULL)
{
Py_DECREF(pIntrospectorClass);
}
Py_DECREF(pIntrospectorModuleName);
Py_DECREF(pIntrospectorModule);
return PYTHON_INTROSPECTION_CLASS_NOT_FOUND_ERROR;
}
/* Instantiates an Introspector object. If the call returns NULL there was an error
creating the object, and thus it returns error. */
pIntrospector = PyObject_CallObject(pIntrospectorClass, NULL);
if (pIntrospector == NULL)
{
if (PyErr_Occurred())
{
PyErr_Print();
}
fprintf(stderr, "Introspector instantiation failed\n");
Py_DECREF(pIntrospectorModuleName);
Py_DECREF(pIntrospectorModule);
Py_DECREF(pIntrospectorClass);
return PYTHON_INTROSPECTION_OBJECT_CREATION_ERROR;
}
/* Decreasing reference to local data. */
Py_DECREF(pIntrospectorModuleName);
Py_DECREF(pIntrospectorModule);
Py_DECREF(pIntrospectorClass);
return OK;
}
I have not added the code to the PyVtk_CreateVtkObject function as it won't enter it, but if I do not add the calls after PyVtk_InitIntroepsctor it won't give the aforementioned error. Finally, if I import Introspector in the Python interpreter myself, it works fine.
Is there a solution to either run it in Debug or Release? I cannot wrap my head around it...
P.S.: I already tried to use Boost::Python, I have two issues open on it as it is giving me problems as well.
Update 1: In particular, the excpetion is thrown when I do this:
PyObject *pIntrospectorModule = PyImport_Import(pIntrospectorModuleName);
Update 2: I have further scoped down the issue to this: whenever I import the vtk package from within the embedded interpreter, it throws the Access Violation on this code:
// Add special attribute __vtkname__
PyObject *s = PyString_FromString(classname);
PyDict_SetItemString(pytype->tp_dict, "__vtkname__", s);
Py_DECREF(s); // <-- In particular on this Py_DECREF
If I try to import anything else, there is no issue, it seems.

Embedding Python into C - can't import method from python module

I'm building C application which will be using Python plugins. When trying to call the method from another Python module, the function PyImport_ImportModule() seems to imports the module properly, then i try to get the function from this module using PyObject_GetAttrString() and all that I get is null.
I already tried using PyModule_GetDict() and PyDict_GetItemString() to get the method from the module, but the effect was the same.
main.c:
#include <stdio.h>
#include <python3.6/Python.h>
int main()
{
PyObject *arg, *pModule, *ret, *pFunc, *pValue, *pMethod, *pDict;
Py_Initialize();
PyObject *sys = PyImport_ImportModule("sys");
PyObject *path = PyObject_GetAttrString(sys, "path");
PyList_Append(path, PyUnicode_FromString("."));
pModule = PyImport_ImportModule("test");
if(pModule == NULL)
{
perror("Can't open module");
}
pMethod = PyObject_GetAttrString(pModule, "myfun");
if(pMethod == NULL)
{
perror("Can't find method");
}
ret = PyEval_CallObject(pMethod, NULL);
if(ret == NULL)
{
perror("Couldn't call method");
}
PyArg_Parse(ret, "&d", pValue);
printf("&d \n", pValue);
Py_Finalize();
return 0;
}
test.py:
def myfun():
c = 123 + 123
print('the result is: ', c)
myfun()
The result i got is:
Can't find method: Success
Segmentation fault (core dumped)
When I used the gdb debugger the output was:
pModule = (PyObject *) 0x7ffff5a96f48
pMethod = (PyObject *) 0x0
Your program is not wroking because the module being imported is the test built-in module, rather than your test.py script. This is because you are appending the current directory to sys.path, so it is checked after every other already existing path in the list. You should insert it at the beginning of the list instead, so that it is checked first.
This will work:
PyObject *sys = PyImport_ImportModule("sys");
PyObject *path = PyObject_GetAttrString(sys, "path");
PyList_Insert(path, 0, PyUnicode_FromString("."));
By the way, you should #include the Python header before anything else, as stated in the documentation:
Note: Since Python may define some pre-processor definitions which affect the standard headers on some systems, you must include Python.h before any standard headers are included.

Call a Python function from c++ using boost.python

I have a python script script.py containing a function hello that simply prints Hello World. I would like to call this function in my c++ program using boost python module. Below is what I have done so far which is working fine and verifies my setup is working. Can someone please guide me how can I execute the function from within my c++ program contained in the python script ?
#include <boost/python.hpp>
#include <cstdlib> // setenv
int main()
{
// Allow Python to load modules from the current directory.
//setenv("PYTHONPATH", ".", 1);
// Initialize Python.
Py_Initialize();
namespace python = boost::python;
try
{
python::object main_module = python::import("__main__");
python::object main_namespace = main_module.attr("__dict__");
python::object print = python::exec("from time import time,ctime\n"
"print 'Today is',ctime(time())\n", main_namespace);
}
catch (const python::error_already_set&)
{
PyErr_Print();
return 1;
}
// Do not call Py_Finalize() with Boost.Python.
}

PyImport_ImportModule always return NULL even though system path is set

I am trying to write a simple C/C++ program in Ubuntu 17.10 to call python function using embedding. However, it always complained that it is unable to import the module using PyImport_ImportModule.
I have included Python.h in the cpp file and compile it like this:
g++ -L/usr/lib/python3.6/config-3.6m-x86_64-linux-gnu/ -g -o main main.cpp -I/usr/include/python3.6 -lpython3.6
I have tried the following things but none of the approach works:
add the current path to path using PySys_GetObject and PyList_Insert (as per this link)
set PYTHONPATH=/tmp then run the main program (as per this link)
/tmp/main.cpp
#include <iostream>
#include <python3.6/Python.h>
using namespace std;
int main()
{
Py_Initialize();
PyObject* sysPath = PySys_GetObject("path");
PyObject *path = PyBytes_FromString("/tmp");
int result = PyList_Append(sysPath, path);
PyObject* module = PyImport_ImportModule("hello");
if (0 != module)
{
PyObject* helloFunc = PyObject_GetAttrString(module, "hello");
if (helloFunc)
{
PyEval_CallObject(helloFunc, 0);
}
else
{
fprintf(stderr, "hello function is NULL");
}
}
else
{
fprintf(stderr, "hello module is NULL -- somehow");
}
Py_Finalize();
return 0;
}
/tmp/hello.py
class hello(object):
def __init__(self):
print("Hello world: init")
def hello(self):
print("Hello world: hello")
Please can someone tell me why PyImport_ImportModule always return NULL in my simple program?
Many thanks!

I can create my C++ object in Python, but can't access methods

Sorry, there's a lot here, but I really think all of this information is relevant to the question I'm about to ask.
So I wrapped my C++ class using SWIG, compiled, etc. and in a python script I create an instance of my class and then try to call a method on that class. Notice I said "try" to call. The call on the method fails. Details on implementation & "fails" follows.
First, I am building the Python3.4 interpreter with my Windows application and calling my python script as follows:
Py_Initialize();
/* Some code here to set up python paths. */
.....
PyObject *pName = PyUnicode_FromString("hello");
if(pName == NULL)
{
Py_Finalize();
return false;
}
PyObject* pModule = PyImport_Import(pName);
if(pModule == NULL)
{
Py_Finalize();
return false;
}
/* "go" is the name of the function in my python script that I want to call. */
PyObject *pFunc = PyObject_GetAttrString(pModule, "go");
if(pFunc == NULL)
{
Py_Finalize();
return false;
}
PyObject *pArgs = PyTuple_New(0);
PyObject *pValue = PyObject_CallObject(pFunc, pArgs);
if(pValue == NULL)
{
Py_Finalize();
return false;
}
Py_Finalize();
return true;
===================
Here's my class structure:
class BoganDocument
{
private:
BoganMetadataSet myMetadata;
public:
// Constructor
BoganDocument();
// Destructor
virtual ~BoganDocument();
// Useful methods.
wstring getMetadataValue(wstring metadata_name);
}
===================
Here's my Python script (named "hello.py").
import BoganDocument
def go():
print("I'm in go()")
d = BoganDocument.BoganDocument()
print("I made a document")
ts = d.getMetadataValue("CreationTimestamp");
print("The creation timestamp is " + ts)
=====================
And the output on my console screen looks like this:
trying C:\Program Files (x86)\MyApp\Python\swig_runtime_data4_d.pyd
trying C:\Program Files (x86)\MyApp\Python\swig_runtime_data4.py
trying C:\Program Files (x86)\MyApp\Python\swig_runtime_data4.pyw
trying C:\Program Files (x86)\MyApp\Python\swig_runtime_data4.pyc
trying c:\MyApp\Workplace\swig_runtime_data4_d.pyd
trying c:\MyApp\Workplace\swig_runtime_data4.py
trying c:\MyApp\Workplace\swig_runtime_data4.pyw
trying c:\MyApp\Workplace\swig_runtime_data4.pyc
import 'BoganDocument' # <_frozen_importlib.SourceFileLoader object at 0x10D2E3F0>
import 'hello' # <_frozen_importlib.SourceFileLoader object at 0x10D2E1C0>
I'm in go()
I made a document
===================
Notice that I have set PYTHONVERBOSE to 11 to get as much diagnostic information as I can. And notice that all messages stop after "print("I made a document")". No syntax error, nothing. The value of pValue after "PyObject *pValue = PyObject_CallObject(pFunc, pArgs);" is NULL.
Now, the call to the constructor at "d = BoganDocument.BoganDocument()" is known to work, as I've set a breakpoint in the BoganDocument constructor and single stepped through.
I also set a breakbpoint in BoganDocument's getMetadataValue method but never get there. And there's the mystery.
No doubt I have a bug, but I haven't a clue.
Did you step into the SWIG wrapper for getMetadataValue? The C++ wrapper gets called when you do d.getMetadataValue() frm Python, the C++ wrapper then calls Bogan::getMetadataValue on the appropriate C++ instance. Since you never get to the C++ instance's getMetadataValue, maybe SWIG wrapper is finding the call to be illegal or such (SWIG doesn't normally silently ignore problems but I don't know what else to suggest). Since you are using wide string, make sure you include std_wstring.i.

Categories