#include <Python.h>
#include <fstream>
#include <iostream>
#include <string>
#include <filesystem>
#include <sys/types.h>
#include <dirent.h>
static const char * sPythonCode =
"class Test :\n"
" def __init__(self) : \n"
" self.Disc_ = 0. \n"
" def getset(self) : \n"
" self.Disc_ = 7. \n"
" return self.Disc_ \n";
std::string writeFile()
{
static int iFile = 0;
std::string sFileName(std::string("test") + std::to_string(iFile));
std::ofstream out("py/" + sFileName + ".py");
out << sPythonCode;
out.flush();
out.close();
iFile++;
return sFileName;
}
static bool bPythonOpen = false;
#define PYTHONPATHLEN 501
static void _PyInit()
{
if (!Py_IsInitialized())
{
Py_InitializeEx(0);
}
}
void openPython(void)
{
if (!bPythonOpen)
{
const size_t szBufferN = 1000;
char acLoadPath[szBufferN];
const char *pypath = "./py";
_PyInit();
PyRun_SimpleString("import sys");
PyRun_SimpleString("print('python (%d.%d.%d) initialized' % (sys.version_info.major, sys.version_info.minor, sys.version_info.micro))");
PyRun_SimpleString("print('--------------------------')");
snprintf(acLoadPath, szBufferN, "sys.path.append('%s')", pypath);
PyRun_SimpleString(acLoadPath);
bPythonOpen = true;
}
}
PyObject *loadPythonModule(const char *acModule)
{
PyObject *pyModule = NULL;
if (bPythonOpen && acModule && strcmp(acModule, ""))
{
printf("%s\n", acModule);
pyModule = PyImport_ImportModule(acModule);
if (!pyModule)
{
PyErr_Print();
}
}
return pyModule;
}
void loadPython()
{
std::string sFileName = writeFile();
openPython();
//sleep(1);
PyObject *pPythonModule = loadPythonModule(sFileName.c_str());
if (pPythonModule)
PyDict_DelItemString(PyImport_GetModuleDict(), PyModule_GetName((PyObject *)pPythonModule));
}
int main(int argc, char **argv)
{
for (int i = 0; i < 10; i++)
{
loadPython();
}
}
My working env:
gcc version 8.3.1 20190311 (Red Hat 8.3.1-3) (GCC)
Red Hat Enterprise Linux Server release 7.6 (Maipo)
problem with python 3.6.10 / 3.8.3
Command to compile:
g++ pythontest.cpp -I/opt/python/python3.6.10/include/python3.6m -L/opt/python/python3.6.10/lib -lpython3.6m
create py directory:
mkdir py
When I run this code I have random error on different test file that I load.
Example of output:
python (3.6.10) initialized
--------------------------
test0
test1
test2
test3
ModuleNotFoundError: No module named 'test3'
test4
test5
ModuleNotFoundError: No module named 'test5'
test6
test7
ModuleNotFoundError: No module named 'test7'
test8
test9
ModuleNotFoundError: No module named 'test9'
Good to know:
If I uncomment the line with the sleep it works well
If I remove the iFile++, it works also as it used after an already created file
If I relaunch a second without rm -rf py directory it works also
If I erase file after each run in the loadPython function and remove iFile++ it works also
If I use strace to launch the executable I don't see the problem
For the moment it seems that the Python loader does not see the file on disk, however in case of failure if I print what I have in the directory thanks to dirent I see the testx.py
Please note that we reproduce the error on different Linux servers (not a hardware problem and even on Windows), with Python 2.7.x it works perfectly well.
You should call __import__('importlib').invalidate_caches() each time you modify modules folders to let C Python knows it must read directories again.
Related
I'm building an embedded python application in C++, using boost::python. The embedded environment exports part of itself as a module, in other words the python code that runs in the environment won't run in a standard python env as the embedded module can't be imported.
One of the features that would be really helpful for debugging would be a debug shell, where I can break out and manually input standard python expressions to inspect/modify the current state. The problem I have is that I don't/can't know in advance whether it's an eval (e.g. "2+2") or an exec (e.g. "a=2+2"). If I exec("2+2") I don't get the result, and if I eval("a=2+2") I get a syntax error. (Coming from a C background I don't quite understand why this distinction exists). Trying eval first, then exec if it fails, seems like a very dubious solution not least because of side effects.
As far as I can see boost::python simply replicates python's eval/exec functions, so this problem is more at the python level, but the answer I'm hoping to get is how to do it at the C++ (ideally boost) level. Is there some horrendously complicated regex that could be used to differentiate exec-able from eval-able code? For now I'm just going with the ugly solution of making the user decide with a prefix on the expression.
Here's an MCVE with a TODO at the point where I need to choose between eval and exec (obv. requires python-dev and boost_python libs)
// g++ $(python3-config --cflags) so.cpp -fPIC -lboost_python3 $(python3-config --ldflags)
#include <boost/python.hpp>
#include <string>
#include <iostream>
namespace py = boost::python;
std::string pyobject_to_string(PyObject* obj)
{
PyObject* repr = PyObject_Str(obj);
PyObject* str = PyUnicode_AsEncodedString(repr, "utf-8", "~E~");
const char *bytes = PyBytes_AS_STRING(str);
Py_XDECREF(repr);
Py_XDECREF(str);
return std::string(bytes);
}
int main(int argc, const char** argv)
{
// Init python env
Py_Initialize();
if (argc < 2)
{
return 1;
}
try
{
// TODO decide, based on argv[1], which needs to be called, this:
py::object res = py::eval(argv[1]);
std::cout << pyobject_to_string(res.ptr()) << std::endl;
// or this:
py::exec(argv[1]);
// or something else entirely...?
}
catch(py::error_already_set&)
{
if (PyErr_Occurred())
{
PyObject *type, *value, *traceback;
PyErr_Fetch(&type, &value, &traceback);
std::string message = pyobject_to_string(type) + ":" + pyobject_to_string(value);
//PyErr_Restore(type, value, traceback);
std::cerr << message << std::endl;
}
else
{
std::cerr << "unknown error" << std::endl;
return 1;
}
}
}
Example output is:
$ ./a.out 2+2
4
$ ./a.out a=2+2
<class 'SyntaxError'>:('invalid syntax', ('<string>', 1, 2, 'a=2+2'))
Many thanks in advance
I am new to tensorflow as well as including python code in c++, therefore I would apprechiate any tips/comments on the following weird behaviour:
I have a c++ class pythoninterface with headerfile pythoninterface.h:
#include <string>
#include <iostream>
class pythoninterface{
private:
const char* file;
const char* funct;
const char* filepath;
public:
pythoninterface();
~pythoninterface();
void CallFunction();
};
The sourcefile pythoninterface.cpp:
#include <Python.h>
#include <string>
#include <sstream>
#include <vector>
#include "pythoninterface.h"
pythoninterface::pythoninterface(){
file = "TensorflowIncludePy";
funct = "myTestFunction";
filepath = "/path/To/TensorflowIncludePy.py";
}
void pythoninterface::CallFunction(){
PyObject *pName, *pModule, *pDict, *pFunc, *pValue, *presult;
// Initialize the Python Interpreter
Py_Initialize();
//Set in path where to find the custom python module other than the path where Python's system modules/packages are found.
std::stringstream changepath;
changepath << "import sys; sys.path.insert(0, '" << filepath << "')";
const std::string tmp = changepath.str();
filepath = tmp.c_str();
PyRun_SimpleString (this->filepath);
// Build the name object
pName = PyString_FromString(this->file);
// Load the module object
pModule = PyImport_Import(pName);
if(pModule != NULL) {
// pDict is a borrowed reference
pDict = PyModule_GetDict(pModule);
// pFunc is also a borrowed reference
pFunc = PyDict_GetItemString(pDict, this->funct);
if (PyCallable_Check(pFunc))
{
pValue=Py_BuildValue("()");
printf("pValue is empty!\n");
PyErr_Print();
presult=PyObject_CallObject(pFunc,pValue);
PyErr_Print();
} else
{
PyErr_Print();
}
printf("Result is %d!\n",PyInt_AsLong(presult));
Py_DECREF(pValue);
// Clean up
Py_DECREF(pModule);
Py_DECREF(pName);
}
else{
std::cout << "Python retuned null pointer, no file!" << std::endl;
}
// Finish the Python Interpreter
Py_Finalize();
}
And the Python File from which the function should be included (TensorflowIncludePy.py):
def myTestFunction():
print 'I am a function without an input!'
gettingStartedTF()
return 42
def gettingStartedTF():
import tensorflow as tf #At this point the error occurs!
hello = tf.constant('Hello, TensorFlow!')
sess = tf.Session()
print(sess.run(hello))
return 42
Finally in my main function I only create a pythoninterface object p and call the function p.CallFunction(). The communication between the c++ and python code works all right, but when (at runtime) the line import tensorflow as tf is reached, I get a *** stack smashing detected *** error message and the program finishes. Can anyone guess what the problem might be or had a similar issue before?
I know there is a c++ tensorflow API, but I feel more comfortable with using tensorflow in python so I thought this might be the perfect solution for me (apparently it is not...:P)
Let's take a look at my project.
I'm using Visual Studio 2008, Python 2.7 and numpy 1.8.1 (but I have tried several versions and none worked). My project is being compiled on debug mode.
It's a very simple code:
/* = Main.cpp file = */
#include "stdafx.h"
#include "Python.h"
int _tmain(int argc, _TCHAR* argv[])
{
PyObject *pName, *main;
Py_Initialize();
pName = PyUnicode_FromString("main");
main = PyImport_Import(pName);
Py_XDECREF(pName);
if (main != NULL)
{
printf("Python Module Loaded!!\n");
}
else
{
printf("Unable to load Python Module!!\n");
}
return 0;
}
And
""" = Main.py file = """
print 'Hello World!'
If I execute this code, I get:
As it is expected.
My problem arises as soon as I change main.py into:
""" = Main.py file = """
import numpy
print 'Hello World!'
Then I get the:
I have tried to run main.py separately on a python interpreter (not embedding it into C++) and then everything works just fine:
I have also tried a modification on the main.cpp as follows:
#include "stdafx.h"
#include "Python.h"
int _tmain(int argc, _TCHAR* argv[])
{
PyObject *pName, *main;
Py_Initialize();
PyRun_SimpleString("import numpy");
return 0;
}
From this code the output is:
Finally I also tried compiling original version of main.cpp code in release mode and then the output is:
So, my question here is: What can I do to get numpy working under debug compilations using an embedded interpreter on Visual Studio 2008?
You know you have to have use a python debug version built don't you? That is probably your problem. (I would leave a comment but no rep ): )
I foud out there is a new Boost.Process 0.5 but I cant see how to execute across Windows Linux and Mac ping or echo.
I got it working at leaast on Windows with simple:
#include <string>
#include <iostream>
#include <boost/asio.hpp>
#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/iostreams/stream.hpp>
#include <boost/process.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/system/error_code.hpp>
namespace bp = boost::process;
namespace bpi = boost::process::initializers;
namespace bio = boost::iostreams;
int main()
{
bp::pipe p = bp::create_pipe();
{
bio::file_descriptor_sink sink(p.sink, bio::close_handle);
boost::filesystem::path p("C:/Windows/System32/cmd.exe");
boost::system::error_code ec;
bp::execute(
bpi::run_exe(p),
bpi::set_cmd_line(L"cmd /c echo --echo-stderr hello"),
bpi::bind_stdout(sink),
bpi::set_on_error(ec)
);
}
bio::file_descriptor_source source(p.source, bio::close_handle);
bio::stream<bio::file_descriptor_source> is(source);
std::string s;
is >> s;
std::cout << s << std::endl;
std::cin.get();
return 0;
}
On windows this works correctly but how to make it crossplatform to work also on Mac and Linux? (I am stupid and do not know how to write one path that would work for any Unix terminal (or at least for Linux Bash and mac default one)) So How to run commandline/terminal utils with Boost.Process 0.5 on Windows and Unix like OSs (better not writing path to terminal each time but just writting app like echo or ping and its arguments)?
...Found related code inside prevoius version:
std::string exe;
std::vector<std::string> args;
#if defined(BOOST_POSIX_API)
exe = "/bin/sh";
args.push_back("sh");
args.push_back("-c");
args.push_back(command);
#elif defined(BOOST_WINDOWS_API)
char sysdir[MAX_PATH];
UINT size = ::GetSystemDirectoryA(sysdir, sizeof(sysdir));
if (!size)
boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::launch_shell: GetWindowsDirectory failed"));
BOOST_ASSERT(size < MAX_PATH);
exe = std::string(sysdir) + (sysdir[size - 1] != '\\' ? "\\cmd.exe" : "cmd.exe");
args.push_back("cmd");
args.push_back("/c");
args.push_back(command);
#endif
Under boost.process 0.5 the shell_path() API was introduced so might the following will hook you up
#if defined(BOOST_POSIX_API)
#define SHELL_COMMAND_PREFIX "-c"
#elif defined(BOOST_WINDOWS_API)
#define SHELL_COMMAND_PREFIX "/c"
#endif
filesystem::path shellPath = process::shell_path();
std::string cl = shell_path().string() + " " SHELL_COMMAND_PREFIX " ";
cl += "ping 127.0.0.1";
execute(
set_cmd_line(cl),
throw_on_error()
);
If you really want to hide the #ifdef, I'd go on and edit the boost sources to return also the relevant command prefix (adding new API) , open source after all isn't it ? :).
You could find the relevant sources to edit at boost/process/windows/shell_path.hpp and boost/process/posix/shell_path.hpp
I have some python code that I want to use from C++, I want to build it in lib or dll? how can I do it?
code have dependencies:
import socket
import string
import struct
import sys
and maybe PIL
You can use cython and write thin wrapper to export it to C
Cython lib helloWorld.pyx:
import sys
sys.path.append(".") #yourlib is in current folder
import yourlib #you can import any python module
cdef public void helloWorld():
print "Hello World Cython!"
yourlib.helloWorld("Python")
cdef public int my_sum(int x, int y):
return x*x+y
from libcpp.string cimport string
cdef public string testString( string sx, string sy ):
x = int(sx.c_str())
y = int(sy.c_str())
ret= "%d*%d+%d=%d"%(x,x,y,my_sum(x,y))
cdef char* ret2= ret
return string( ret2 )
Compile with cython (create helloWorld.cpp, helloWorld.h):
cython --cplus helloWorld.pyx
Your code program.cpp:
#include <string>
#include <iostream>
#include "Python.h"
#include "helloWorld.h" // it's cpp header so remove __PYX_EXTERN_C (bug)
int main(int argc, char *argv[]) {
Py_Initialize(); //start python interpreter
inithelloWorld(); //run module helloWorld
helloWorld();
std::cout << testString("6","6") << std::endl; #it's fast!
Py_Finalize();
return 0;
}
Compile and run:
g++ program.cpp helloWorld.cpp -I/usr/include/python2.7/ -lpython2.7
./a.out
Hello World Cython!
Hello World Python!
6*6+6=42
Another way is to use boost::python
Your code program.cpp:
#include <string>
#include <iostream>
#include <boost/python.hpp>
int main(int argc, char *argv[]) {
Py_Initialize();
boost::python::object sys = boost::python::import("sys");
sys.attr("path").attr("append")(".");
boost::python::object main_module = boost::python::import("yourlib");
main_module.attr("helloWorld")("boost_python");
boost::python::object ret= main_module.attr( "my_sum" )( 10, 10 );
std::cout << boost::python::extract<char const*>(ret) << std::endl;
Py_Finalize();
return 0;
}
Compile and run:
g++ program.cpp -I/usr/include/python2.7/ -lpython2.7 -lpython_boost
./a.out
Hello World boost_python!
10*10+10=110
You might want to check how to embed python in another application (http://docs.python.org/extending/embedding.html).