I am trying to embed python script into c++ project.
Below is what I have tried so far.
#include<iostream>
#include <Python.h>
int
main()
{
Py_Initialize();
PyObject* sysPath = PySys_GetObject("path");
PyObject* modPath = PyBytes_FromString("C:\\Users\\naal\\Documents\\Visual Studio 2017\\Projects\\Project1\pyscripts");
int result = PyList_Insert(sysPath,0, modPath);
PyObject *pModule = PyImport_ImportModule("myscript2");
printf("%p\n", pModule);
return 0;
}
below is the python script "myscript2.py"
def find_me():
print("hey you found me")
The problem is, the main module is not able to find the python script i.e object pyModule is always NULL, no matter how I change python script path.
What am I doing wrong ?
I ended up implementing this in another way.
#include<iostream>
#include <Python.h>
int main() {
std::string location = "C:\\Users\\myscript.py";
const char* file_location = location.c_str(); FILE* file_pointer;
Py_Initialize();
file_pointer = _Py_fopen(file_location, "r");
PyRun_SimpleFile(file_pointer, file_location);
Py_Finalize();
return 1;
}
The above seemed to work. I still don't know why the SYSPATH idea originially mentioned in the question didn't work.
Related
I have a question regarding the below code.
It's an example how to pass a custom class via shared_ptr to embedded python code and it works when boost is dynamically linked.
Unfortunately the same code with statically linked boost doesn't work with the following error message:
"No to_python (by-value) converter found for C++ type: class boost::shared_ptr".
I don't understand why a different linking can affect type recognition of a registered converter. What am I missing?
Can anybody help me out?
Thanks,
Dominik
Example from here.
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/python.hpp>
#include <string>
#include <iostream>
namespace bp = boost::python;
struct Foo{
Foo(){}
Foo(std::string const& s) : m_string(s){}
void doSomething() {
std::cout << "Foo:" << m_string << std::endl;
}
std::string m_string;
};
typedef boost::shared_ptr<Foo> foo_ptr;
BOOST_PYTHON_MODULE(hello)
{
bp::class_<Foo, foo_ptr>("Foo")
.def("doSomething", &Foo::doSomething)
;
};
int main(int argc, char **argv)
{
Py_Initialize();
try {
PyRun_SimpleString(
"a_foo = None\n"
"\n"
"def setup(a_foo_from_cxx):\n"
" print 'setup called with', a_foo_from_cxx\n"
" global a_foo\n"
" a_foo = a_foo_from_cxx\n"
"\n"
"def run():\n"
" a_foo.doSomething()\n"
"\n"
"print 'main module loaded'\n"
);
foo_ptr a_cxx_foo = boost::make_shared<Foo>("c++");
inithello();
bp::object main = bp::object(bp::handle<>(bp::borrowed(
PyImport_AddModule("__main__")
)));
// pass the reference to a_cxx_foo into python:
bp::object setup_func = main.attr("setup");
setup_func(a_cxx_foo);
// now run the python 'main' function
bp::object run_func = main.attr("run");
run_func();
}
catch (bp::error_already_set) {
PyErr_Print();
}
Py_Finalize();
return 0;
}
I far as I understand the documentation about Boost Python linkage, it seems that the conversion registry used for automatic conversion of Python object into C++ object is not available when statically linked. I'm facing the same issue and that's a pity it is actually the case. I would have imagined at least the required converter to be bundle but I'm afraid it is not the case for some reason.
I am writing a custom PAM module.
I have written a shared object (.so) file as required for Linux-PAM. What this .so file does, is call embedded Python to open up my facial recognition and , depending on the outcome, will return PAM_SUCCESS or PAM_AUTH_ERR
In the /etc/pam.d/sudo file I have told PAM that the file resides in (/home/berns/2020-ca326-cberns-fileencryption-with-opencv/PAM/pam_authnew.so). This is okay as , when sudo is typed, I can see my own personal error statement being printed saying it could not load the Python file.
The issue I have is, the facial recognition code resides in a total different directory from where the .so file resides. (../code/facial). I have used a chdir command in my C code, but it does'nt seem to change the directory back to where the Python facial files reside to provide success or not.
Is there something I'm missing ?
C code given below:
#define PAM_SM_AUTH
#define PAM_SM_ACCOUNT
#define PAM_SM_SESSION
#define GetCurrentDir getcwd
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <security/pam_appl.h>
#include <security/pam_modules.h>
#include </usr/include/python3.6m/Python.h>
int main(int argc, char** argv){
}
PAM_EXTERN int pam_sm_authenticate( pam_handle_t *pamh, int flags,int argc, const char **argv ){
char *result;
chdir("../code/facial"); // this changes it to the correct directory to execute
//dlopen("/usr/lib/x86_64-linux-gnu/libpython3.6m.so",RTLD_LAZY | RTLD_GLOBAL);
Py_Initialize(); // Starts python interpreter
PyRun_SimpleString("import os\nimport sys\nsys.path.append(os.getcwd())"); // lets python know where we are
PyObject *mymod, *func1, *ret1;
mymod = PyImport_ImportModule("pam_detect"); // This is the .py
if (mymod != NULL){ // check if the file file was loaded
func1 = PyObject_GetAttrString(mymod, "detect"); // hel is the function name in the file you declared earlier
ret1 = PyObject_CallObject(func1, NULL); // Null because the function doesnt take an argument.
result = PyUnicode_AsUTF8(ret1);
//printf("%s\n", result);
if (strcmp(result, "success") == 0){
Py_Finalize();
return PAM_SUCCESS;
}
else{
Py_Finalize();
return PAM_AUTH_ERR;
}
}
else{
printf("Error: can't find file!\n");
}
Py_Finalize();
return 0;
}
Error response from the terminal below
Is this due to child processes not changing the directories outside of their own running space ?
Also worth noting if I compile the C code WITHOUT making it a .so , I can load up the Python files perfectly and recognise my face.
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.
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've been messing around with the Python/C API and have this code:
#include <Python.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
//Initialize Python
Py_Initialize();
//Run file
FILE *fp = fopen("Test.py", "r");
PyRun_SimpleFile(fp,"Test.py");
fclose(fp);
//Run Python code
PyRun_SimpleString("print(__NAME__)");
PyRun_SimpleString("print(__DESC__)");
PyRun_SimpleString("print(__SKIN__)");
PyRun_SimpleString("onEnable()");
//Finalize Python
Py_Finalize();
return EXIT_SUCCESS;
}
Test.py contains this:
__NAME__ = "Frank"
__DESC__ = "I am a test script"
__SKIN__ = "random image"
def onEnable():
print("In Func")
As you would expect, compiling and running the c program results in this:
Frank
I am a test script
random image
In Func
However, I need a way to get the python strings from interpreter, stick them in C strings and then print them, rather than using PyRun_SimpleString("print(blah)").
For example:
char *__NAME__;
__NAME__ = Py_GetObject("__NAME__")
Is this possible?
Thanks for your help.
You need to use PyString_AsString. I think it goes something like this:
PyObject* module = PyImport_AddModule("__main__");
PyObject* o = PyObject_GetAttrString(module , "__NAME__");
if (PyString_Check(o))
{
const char* name = PyString_AsString(o);
// don't delete or modify "name"!
}
Py_DECREF(o);