Python C API - Call C function from embedded python (callback) - python

I have a .cpp file loading python file and calling a function. I have a logger class in the .cpp and I want to use it from .py file.
Example:
# python-file.py
def FunctionCalledFromC_API():
log("some string")
log("some error", error)
log("some debug info", debug)
# etc...
And the .cpp
// cpp-file.cpp
// Load python file, do stuff...
PyObject *args = PyTuple_New(0);
PyObject_CallObject(pFunctionCalledFromC_API, args);
Py_DECREF(args);
I want that the log("some string") function of the .py file calls my logger.log(...) function from the C++ application.

As Markku K. suggested, I made all my app a DLL and I'll try soon.
That's what I'll do (for people with the same question):
Make my app a single DLL
Make an executable to run it (obvious)
Make a Python file containing the functions (example: logger.log()). That functions should call the DLL's equivalent functions.
Do "trial&error" until it works ;)
EDIT:
After a lot of "trial&error", I've found a way to do it!
Using boost::python to expose my API and the standard Python API to load the .py file I made this work!
But I'll have to start the application with a python file importing the shared object and calling the main function :(
Anyway, Thanks Markku K. for your help!

Related

How to bind using pybind11?

This is the structure of my program:
I'm trying to bind my program in C++ with a GUI in python. I'm using pybind11 and I have a python_binding.cpp file for the bind and some ".h" and ".cpp" with the methods in other directories. I include the ".h" files but somehow the python_binding.cpp it's not able to recognize them.
The file config.cpp only has one void method, "cargar_configuracion()" and this is how it looks like in the binding:
#include "Ejemplo/config.h"
PYBIND11_MODULE(Example, m) {
m.doc() = "Binding"; // optional module docstring
m.def("cargar_configuracion", &cargar_configuracion);
The result of this is the following error:
undefined reference to `cargar_configuracion()'
What am I doing wrong? Should I have my .cpp and .h with the binding.cpp in the same directory?
Thanks in advance!
Your pybind11 looks fine, this is a linker error. It looks like config.cpp is in another project within your solution, and is being built within a separate executable. You have two options here, either copy config.cpp into the same directory or reconfigure Ejemplo to be a static library and specify it as a dependency in the properties of the python wrapper project.
Change your code from:
bViewResult = QtWidgets.QPushButton('View Results', self)
bViewResult.clicked.connect(self.openCSV)
to:
bViewResult = QtWidgets.QPushButton('View Results', self)
bViewResult.clicked.connect(cargar_configuracion())

Python Kodi - Force Python handling .pyo like .py

Hey guys i definitely getting crazy
i want to force Python to handle a .pyo like a .py
i got kodi xbmc forked and getting crazy
i already tried out and changed multiple lines and nothing changed?
i linked them here:
const std::string ADDON_PYTHON_EXT = "*.py";
changed this line to *.pyo
return URIUtils::HasExtension(m_strPath, ".py"); changed this line to. .pyo
i thought that would be enough after compiling, still nothing happens when try to open a add-on
still python does not load .pyo files and does not handle them as .py
btw: i figured out deleting all .py files in the folder and just let the first loaded .py file there so 1 -> .py and the others are all .pyo the add-on works.
but when deleting this firstly loaded .py and using this as a .pyo
it fails.
So what do i have to change that it always use .pyo
First UPDATE:
i tried it out,
it does indeed work as a workaround
Created a dummy -> include.py
with just 1 line import service was enough.
But i want to fix this directly in kodi.
in documentions
"If there is a current pyc file, this is taken as the compiled version, so no compile step has to be taken before running the command. Otherwise the py file is read, the compiler has to compile it"
So easy said,
execute .py if not available check if .pyo
but i cannot find where to change / add this method
i cannot find even the method right for the execution
Second Update:
i added these lines in githublink to file
//pyoextension == script
std::string pyoextension = script;
//Check if the file does exist
if (!CFile::Exists(script, false))
{
//pyoextension append o to get -> .pyo
pyoextension = script + "o";
//Check if pyoextension exist -> .pyo
//if not nothing exist throw error
if (!CFile::Exists(pyoextension, false)){
CLog::Log(LOGERROR, "%s - Not executing non-existing script %s", __FUNCTION__, script.c_str());
return -1;
}
LanguageInvokerPtr invoker = GetLanguageInvoker(pyoextension);
return ExecuteAsync(pyoextension, invoker, addon, arguments);
}
LanguageInvokerPtr invoker = GetLanguageInvoker(script);
return ExecuteAsync(script, invoker, addon, arguments);
Checking if the the add-on does not exist as a .py file checking if the .pyo file is there, and execute that instead. But still the checking does work, but it still throws an error out that it cannot start the script
Changing such line (const std::string ADDON_PYTHON_EXT = "*.py"; changed this line to *.pyo) is not a good solution
Main reason is that the kodi is configured to use .py extension to be load from addon.xml file which tradition is followed by all the addon. Changing such headers file will only result to work addon only in your custom build kodi and you have to modify all other addon along with it.
The solution I prefer most is you should follow below steps:
Create a function in your python script (let's call it as function named service() which execute all the necessary code from your file named service.py)
Now create a python file (include.py) to include it in addon.xml containing code as below:
include.py
import service
service.service()
It might not even be worth it, Kodi is moving to python3 soon, and python3 dropped pyo files. It might use pyc then, but that's not sure right now.

How to enable the pyd to call functions in external C++ dll?

I'm trying to design an external DLL for my python program. Now I could use the C++ and Visual Studio 2010 to produce a file with a postfix of ".pyd". If the .pyd is not attached with other .dll files produced by C++, this python library could work well.
However, now I need to design a .pyd file with such a struction:
A.pyd -> 1.dll
-> 2.dll
in which the files 1,2 are C libraries. The functions in these libraries are called when producing A.pyd.
Although this .pyd file could be produced without errors by VS 2010, it could not work in python 3.6. The error report is as follow:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: DLL load failed, could not find the target program.
Even when the referred 1.dll and 2.dll are stored in the same folder that contains A.pyd, this error still exists. I wonder how could I enable the python-C++ library to call the functions in these dynamic link libraries based on C.
OK! Now I have found the correct way to perform this operation!
Wrap the DLL
At first, if the dll (I call it dll-A) that you need to call has not exported any function (you could use dumpbin in VS command to check the exported functions), you need to wrap the original dll-A. Use the implicit call to include dll-A. If there is a function declared in the original .h/.lib, like this:
int func(int a, int b);
Then you need to create a new dll project and wrap the above function by this:
.h
extern "C" _declspec(dllexport) int w_func(int a, int b);
.cpp
int w_func(int a, int b){
return func(a, b);
}
Certainly, if the dumpbin shows that dll-A have avaliable exported functions, you could skip this step.
After exporting this dll (I call it dll-B), you will get 'dll-B' and its depending files (including dll-A and dll-A's depending files).
Write the .pyd file
Use the explicit call to refer dll-B. When using this method, you should not include any lib/.h files, because dll-B itself could provide you with enough interfaces. You could load the dll by this method:
.h
typedef int (*func_ptr)(int a, int b);
.cpp (part of a function named Testdll when you write the .pyd project)
func_ptr FUNC_API = NULL;
HINSTANCE h = LoadLibraryA("libdll/dllB.dll");
//Certainly! you could set the folder of stored dlls!
if (h){
FUNC_API = (func_ptr)GetProcAddress(h, "w_func");
//You could load more functions here.
}
else{ // If the dll could not be found
FreeLibrary(h);
return 0x100;
}
int errorflag=0;
if (FUNC_API==NULL){ //Check whether the function is valid.
cout << "Could not find: func" << endl;
errorflag = errorflag | 0x001;
}
//You could check more functions here.
if (errorflag!=0){ // if any function could not be found.
FreeLibrary(h);
return 0x100 | errorflag;
}
//process functions.
int a,b,c;
c = FUNC_API(a,b);
//Free the lib
if (h)
FreeLibrary(h);
After build your own .pyd, you could get your python database (I call it pyd-C).
Link the .pyd with Python
In python project, you could test this file by this method:
.py
import pydC
if __name__ == '__main__':
X = pydC.cclass()
X.Testdll();
Then you can find that the function is performed well.
Noted that your .pyd should be in the same folder with where .py is. Because you have set dll-B in libdll/dllB.dll, dll-B should be put in the folder named libdll. However, because dll-B calls dll-A and other depending dlls implicitly, dll-A and other files should be in your workspace folder, i.e. the same folder with where .py is.
In short, you need to enter the workspace folder, and the folder formation is as follow:
./
Test.py
pydC.pyd
dllA.dll
dllA_depended1.dll
...
libdll/
dllB.dll

Python C API - Reload a module

I use Python 3.4 and Visual 2010.
I'm embedding Python using the C API to give the user some script capabilities in processing his data. I call python functions defined by the user from my C++ code. I call specific function like Apply() for example that the user has to define in a Python file.
Suppose the user has a file test.py where he has defined a function Apply() that process some data.
All I have to do is to import his module and get a "pointer" to his python function from the C++.
PySys_SetPath(file_info.absolutePath().toUtf8().data()));
m_module = PyImport_ImportModule(module_name.toUtf8().data());
if (m_module)
{
m_apply_function = PyObject_GetAttrString(m_module, "Apply");
m_main_dict = PyModule_GetDict(m_module);
}
So far, so good. But if the user modifies his script, the new version of his function is never taken into account. I have to reboot my program to make it work... I read somewhere that I need to reload the module and get new pointers on functions but the PyImport_ReloadModule returns NULL with "Import error".
// .... code ....
// Reload the module
m_module = PyImport_ReloadModule(m_module);
Any ideas ?
Best regards,
Poukill
The answer was found in the comments of my first post (thank you J.F Sebastian), the PySys_SetPath has to contain also the PYTHONPATH. In my case, that is the reason why the PyImport_ReloadModule was failing.
QString sys_path = file_info.absolutePath() + ";" + "C:\\Python34\\Lib";
PySys_SetPath(UTF8ToWide(sys_path.toUtf8().data()));
m_module = PyImport_ReloadModule(m_module); // Ok !

Exchanging info between running C++ code and python script

I have a python script that is called inside a C++ program. The python script creates a directory based on the current time, puts files in there, and then execution returns to C++. I want to save a parameters file into the directory created in the python program.
I figure my options are:
Pass in the text to save in the parameters file to the Python program and have it create and save the file
Return the location of the directory to C++ from python so it knows where to save the file
Use C++ to locate the most recently created directory after execution of the python script and put file there
I'm not sure how to do any of this. My python script is not embedded. I use
std::string test = "python analyzeData2.py";
system(test.c_str());
to call a python script.
Any ideas how to do this?
I'd go with option B -- return the location of the directory to c++ from python so it knows where to save the file.
If you plan on using system(), something like this:
char* dirname[64];
FILE* fin;
system("python analyzeData2.py > created.log");
fin = fopen("created.log", "r");
fgets(dirname, sizeof(dirname), fin);
fclose(fin);
/* dirname has contents of created.log */
...

Categories

Resources