Reputation: 581
I'm trying to call a function in Python under C++.
This is my attempt:
void myFuncion()
{
PyObject* fExportar = nullptr;
PyObject* modulo = nullptr;
PyObject* pName = nullptr;
const char *scriptDirectoryName = "path/of/my/pyfile";
Py_Initialize();
PyObject *sysPath = PySys_GetObject("path");
PyObject *path = PyUnicode_FromString(scriptDirectoryName);
int result = PyList_Insert(sysPath, 0, path);
if (result == 0 )//0 if ok, -1 if error
{
pName = PyUnicode_FromString("exportarXLS");//exportarXLS.py
modulo = PyImport_Import(pName);
Py_DECREF(path);
if (modulo)
{
fExportar = PyObject_GetAttrString(modulo, "exportar");//it crahs here
Py_DECREF(modulo);
if (fExportar)
{
//call the function
}
}
}
else
{
PyErr_Print();
}
Py_Finalize();
}
}
The problem is that my C++ program crashes if the python script has any wrong import
. In this case, I suspect that I trying to use an invalid version of PyQt4.
This is the module: (exportarXLS.py)
#!/usr/bin/python3
from PyQt4 import QtCore, QtGui, QtSql
def exportar():
print ("hello, I am probing")
Now my question:
Now I am beggining to probe the steps for develop a function for load python plugings, and I wonder how would I avoid the crash if someone want to add a script with wrong import
I have try to enclose the troubled line in a try/catch
block too, but it doesn't work.
EDIT:
I forgot to say that it's only happens into my Qt Project If I try to run my function out of this I can get errors loading modules but it doesn't crash. I've edited the tittle
Upvotes: 0
Views: 492
Reputation: 164
This problem sounds familiar and I even can sort of reproduce it with your code:
If you call myFunction() more than once, what happens is that you import your modules more than once. According to the Docs this might cause issues:
"Some extensions may not work properly if their initialization routine is called more than once; this can happen if an application calls Py_Initialize() and Py_Finalize() more than once." https://docs.python.org/2/c-api/init.html
So if that is the case in your app, a workaround is to initialize the Python Interpreter only once:
#include <iostream>
#include <Python/Python.h>
void myFuncion()
{
PyObject* fExportar = nullptr;
PyObject* modulo = nullptr;
PyObject* pName = nullptr;
const char *scriptDirectoryName = "path/of/my/pyfile";
PyObject *sysPath = PySys_GetObject("path");
PyObject *path = PyUnicode_FromString(scriptDirectoryName);
int result = PyList_Insert(sysPath, 0, path);
if (result == 0 )//0 if ok, -1 if error
{
pName = PyUnicode_FromString("exportarXLS");//exportarXLS.py
modulo = PyImport_Import(pName);
Py_DECREF(path);
if (modulo)
{
fExportar = PyObject_GetAttrString(modulo, "exportar");//it crahs here
Py_DECREF(modulo);
if (fExportar)
{
//call the function
}
}
}
else
{
PyErr_Print();
}
}
int main(int argc, const char * argv[]) {
Py_Initialize();
myFuncion();
myFuncion();
// what ever
Py_Finalize();
return 0;
}
EDIT: "I even can sort of reproduce it" meaning I can get it to crash, on a different line, though, by importing numpy.
Upvotes: 1
Reputation: 94614
It would be best to deal with the problem by finding out the underlying cause of the SEGV as the state of your application could be seriously damaged by it being triggered.
If you want to attempt to catch the SEGV in a semi-structured manner then you can use something like the sample code, which uses sigsetjmp
and siglongjmp
:
#include <python3.7m/Python.h> // That's my python
#include <setjmp.h>
#include <signal.h>
static sigjmp_buf env;
static void
catch_segv(int func)
{
siglongjmp(env, 1);
}
int myFunction()
{
PyObject* fExportar = nullptr;
PyObject* modulo = nullptr;
PyObject* pName = nullptr;
const char *scriptDirectoryName = "."; // NOTE: I changed the path for me
Py_InitializeEx(1); // NOTE: skip signal handlers being registered - for embedding
PyObject *sysPath = PySys_GetObject("path");
PyObject *path = PyUnicode_FromString(scriptDirectoryName);
int result = PyList_Insert(sysPath, 0, path);
if (result == 0 )//0 if ok, -1 if error
{
pName = PyUnicode_FromString("beep");//exportarXLS.py
modulo = PyImport_Import(pName);
Py_DECREF(path);
if (modulo)
{
// redirect segv handler here:
sig_t old = signal(SIGSEGV, catch_segv);
// record an environment to return to with siglongjmp
if (sigsetjmp(env, 1)) { // returns 0 on setting up, 1 when called with siglongjmp(env, 1)
// handler called
Py_Finalize();
signal(SIGSEGV, old); // restore old handler
return 1; // return to caller
} else {
// this triggers a segv (for the test)
(reinterpret_cast<sig_t>(0))(1);
fExportar = PyObject_GetAttrString(modulo, "beep");//it crahs here
Py_DECREF(modulo);
if (fExportar)
{
//call the function
}
}
signal(SIGSEGV, old); // restore old handler
}
}
else
{
PyErr_Print();
}
Py_Finalize();
return 0; // return success.
}
int main(int argc, char **argv)
{
return myFunction();
}
Upvotes: 1
Reputation: 2870
Duck tape solution :
void signal_handler(int signal)
{
std::cout << "Usefull information" std::endl;
exit(1);
}
...
std::signal(SIGSEGV, signal_handler);
Doc : https://en.cppreference.com/w/cpp/utility/program/signal
I think this kind of solution should be used only in debug
Upvotes: 1