calling a method from a python interpreter - c++

per Voo's advice on this thread:
How can I "hook into" Python from C++ when it executes a function? My goal is to profile
I have opened up a new thread for a new question, which is, in C++, how can I initialize a PythonInterpreter and then call a method from it. Specifically, I'd like to be able to call cProfile's methods and also get data from it.

Okay that'll be a bit longer. Note that I pretty much ignore all "usual" error checking - pretty much any python method may return NULL in which case you should handle that gracefully. I show the "unusual" part checking if the given object is callable. Note that PyDECREF fails if the object pointer is NULL, Py_XDECREF does not. And now to the code - there may be a better way to solve all this, but this works fine for me and sadly the documentation is extremely lacking.
C++ code:
#include <Python.h>
static PyThreadState *mainstate;
void initPython(){
PyEval_InitThreads();
Py_Initialize();
mainstate = PyThreadState_Swap(NULL);
PyEval_ReleaseLock();
}
void exitPython(){
PyEval_AcquireLock();
PyThreadState_Swap(mainstate);
Py_Finalize();
}
void callScript() {
PyGILState_STATE gstate = PyGILState_Ensure();
PyObject *pName = PyUnicode_FromString("Startup");
PyObject *pModule = PyImport_Import(pName);
Py_DECREF(pName);
PyObject *pFunc = PyObject_GetAttrString(pModule, "startup");
if (pFunc && PyCallable_Check(pFunc)) {
PyObject *arglist = Py_BuildValue("(u)", "TestScript");
PyObject *result = PyObject_CallObject(pFunc, arglist);
Py_DECREF(arglist);
// Now you have the returned object of the function - do something with it.
// In our case that's None, but you should extend the python scrip to return
// whatever you need - see the profiler API.
Py_DECREF(result);
}
Py_XDECREF(pFunc); // XDECREF does not fail if pointer is NULL.
Py_DECREF(pModule);
PyGILState_Release(gstate);
}
int main() {
printf("Start.\n");
initPython();
callScript();
exitPython();
printf("Exit.\n");
return 0;
}
Your specific script that is always called, change this so that you return all the data you want in a useful manner - at the moment we just use cProfile.run() which just prints some information:
Startup.py
import cProfile
def startup(module_name):
print("Start script")
cProfile.run("import " + module_name)
print("Finished script")
Finally the trivial script that is executed:
TestScript.py
sum = 0
for i in range(10000):
sum += i

Related

crash took place when new context was created in MFC

v8 version : 10.5.0
IDE : vs2022
I am trying to embed v8 in MFC. To test simply, I created a dialog project, initialized v8 on OnInitDialog(), disposed it OnDestroy(). And then, I wrote the code to create the context in BN_CLICKED event and run script("Hello World!)". So, when the button was pressed, I wanted to show "Hello World!". But the crash took place in the code that create the context(v8::Local<v8::Context> context = v8::Context::New(isolate, nullptr);).
As a result of testing, this only happens in MFC. This crash does not happen in console programs.
Somebody, anybody help me~. there is my whole code below.
cpp
BOOL Cv8TestDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
...
// Initialize V8.
v8::V8::InitializeICUDefaultLocation("");
v8::V8::InitializeExternalStartupData("");
std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform();
v8::V8::InitializePlatform(platform.get());
v8::V8::Initialize();
// Create a new Isolate and make it the current one.
m_create_params.array_buffer_allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator();
m_isolate = v8::Isolate::New(m_create_params);
return TRUE;
}
void Cv8TestDlg::OnDestroy()
{
CDialogEx::OnDestroy();
m_isolate->Dispose();
v8::V8::Dispose();
v8::V8::DisposePlatform();
delete m_create_params.array_buffer_allocator;
}
void Cv8TestDlg::OnRun()
{
CString text;
v8::Isolate* isolate = m_isolate;
v8::Isolate::Scope isolate_scope(isolate);
// Create a stack-allocated handle scope.
v8::HandleScope handle_scope(isolate);
// Create a new context.
v8::Local<v8::Context> context = v8::Context::New(isolate, nullptr);
// Enter the context for compiling and running the hello world script.
v8::Context::Scope context_scope(context);
{
// Create a string containing the JavaScript source code.
v8::Local<v8::String> source = v8::String::NewFromUtf8Literal(isolate, "'Hello' + ', World!'");
// Compile the source code.
v8::Local<v8::Script> script = v8::Script::Compile(context, source).ToLocalChecked();
// Run the script to get the result.
v8::Local<v8::Value> result = script->Run(context).ToLocalChecked();
// Convert the result to an UTF8 string and print it.
v8::String::Value utf8(isolate, result);
text = (LPCWSTR)*utf8;
}
GetDlgItem(IDC_EDIT1)->SetWindowText(text);
}
h
class Cv8TestDlg : public CDialogEx
{
...
v8::Isolate::CreateParams m_create_params;
v8::Isolate* m_isolate = nullptr;
virtual BOOL OnInitDialog();
...
afx_msg void OnDestroy();
afx_msg void OnRun();
};
crash call stack
v8.dll!v8::internal::tracing::TraceEventHelper::GetTracingController()
v8.dll!v8::NewContext(v8::Isolate * external_isolate, v8::ExtensionConfiguration * extensions, v8::MaybeLocal<v8::ObjectTemplate> global_template, v8::MaybeLocal<v8::Value> global_object, unsigned __int64 context_snapshot_index, v8::DeserializeInternalFieldsCallback embedder_fields_deserializer, v8::MicrotaskQueue * microtask_queue) line 6391 C++
v8.dll!v8::Context::New(v8::Isolate * external_isolate, v8::ExtensionConfiguration * extensions, v8::MaybeLocal<v8::ObjectTemplate> global_template, v8::MaybeLocal<v8::Value> global_object, v8::DeserializeInternalFieldsCallback internal_fields_deserializer, v8::MicrotaskQueue * microtask_queue) line 6413 C++
v8Test.exe!Cv8TestDlg::OnRun() line 198 C++
exception message
(0x00007FFADBB43D53(v8.dll), v8Test.exe): 0xC0000005: 0x0000000000000088 access violation
break position(trace-event.cc)
v8::TracingController* TraceEventHelper::GetTracingController() {
-> return v8::internal::V8::GetCurrentPlatform()->GetTracingController();
}
I think the problem is std::unique_ptr<v8::Platform> platform in OnInitDialog. The purpose of a std::unique_ptr is to destroy the thing it points at when the pointer goes out of scope (i.e. in this case, at the end of the function). The v8::Platform should be long-lived, all the way until the v8::V8::DisposePlatform() call.

Embedded Python: Py_CompileString + PyImport_ExecCodeModule: Why Aren't My Objects Disposed?

I have added embedded Python scripting ability to my C++ app. Users write a script, and then my passes values into that script, executes it, and receives the results. I use Py_CompileString() to compile their script PyImport_ExecCodeModule() to import the compiled script, and PyObject_GetAttrString() to find a specially named function ('python_main') and execute that function and get the result. This all works fine.
Recompiling the code when the user changes the script also works, as the changes to the Python source are reflected in the Python output. What I find disturbing is that I am not able to fully discard the code modules performing the Python recompile. (Maybe this is because I'm an experienced C++ programmer but a newbie when it comes to Python.)
The simplified sample code below illustrates the problem. While the ref count for the compiled script is 1, the ref count for the module, the function attribute, and the result value are all 2. I would expect these to be 1 so that, when I can Py_DECREF() on these objects and fully release them, ensuring they are truly destroyed and that everything is "starting from scratch" after I recompile the Python code.
Py_Initialize();
// compile and execute python_main() in the following script
/*
def python_main():
print('python_main called')
return -100
def main():
print('**SCRIPT** main called')
def __init_(self):
print('**SCRIPT** init called')
def __del__(self):
print('**SCRIPT** del called')
if __name__ == '__test__':
print('**SCRIPT** __name__ == __test__')
main()
*/
PyObject* codeObj = NULL;
PyObject* moduleObj = NULL;
PyObject* funcObj = NULL;
PyObject* valueObj = NULL;
// compile the script (new reference)
printf("Compile Script...\n");
codeObj = Py_CompileString(sPythonCode, "myfile.py", Py_file_input);
if (codeObj != NULL) {
printf("Code Object Ref Count = %zd\n", Py_REFCNT(codeObj));
printf("Import Module...\n");
moduleObj = PyImport_ExecCodeModule("__test__", codeObj);
if (moduleObj != NULL) {
printf("Module Object Ref Count = %zd\n", Py_REFCNT(moduleObj));
printf("Get Function Object...\n");
funcObj = PyObject_GetAttrString(moduleObj, "python_main");
if (funcObj) {
printf("Function Object Ref Count = %zd\n", Py_REFCNT(funcObj));
printf("Call function object...\n");
valueObj = PyObject_CallObject(funcObj, NULL);
if (valueObj) {
printf("Function Return Value Ref Count = %zd\n", Py_REFCNT(valueObj));
}
}
}
}
if (valueObj != NULL) {
Py_DecRef(valueObj);
}
if (funcObj != NULL) {
Py_DecRef(funcObj);
}
if (moduleObj != NULL) {
Py_DecRef(moduleObj);
}
if (codeObj != NULL) {
Py_DecRef(codeObj);
}
PyGC_Collect();
printf("\n");
printf("Garbage Collect...\n");
printf("After GC: Code Object Ref Count = %zd\n", Py_REFCNT(codeObj));
printf("After GC: Module Object Ref Count = %zd\n", Py_REFCNT(moduleObj));
printf("After GC: Function Object Ref Count = %zd\n", Py_REFCNT(funcObj));
printf("After GC: Function Return Value Ref Count = %zd\n", Py_REFCNT(valueObj));
The output produced by this code is as follows. It would seem to indicate that the Code Object was destroyed, but that the module, function reference, and return value are still "lingering".
Compile Script...
Code Object Ref Count = 1
Import Module...
**SCRIPT** __name__ == __test__
**SCRIPT** main called
Module Object Ref Count = 2
Get Function Object...
Function Object Ref Count = 2
Call function object...
**SCRIPT** python_main called
Function Return Value Ref Count = 2
Garbage Collect...
After GC: Code Object Ref Count = 4388382480
After GC: Module Object Ref Count = 1
After GC: Function Object Ref Count = 1
After GC: Function Return Value Ref Count = 1
Can someone tell me if this is to be expected, or if I am doing something wrong?
Thanks!

Calling a Python function within a C++ program

I have a real simple Python function:
def myfunc(x):
return 2.0 * x
I want to send this function to a C++ program and call it so I have done this:
#include "Python.h"
static PyObject *execMyPyFunc(PyObject *self, PyObject *args) {
PyObject *Fx, *pyresult;
double x;
PyArg_ParseTuple(args, "dO", &x, &Fx);
pyresult = PyObject_CallFunction(Fx, "d", x);
return pyresult;
}
static PyMethodDef C_API_TestMethods[] = {
{"execMyPyFunc", execMyPyFunc, METH_VARARGS, "Add documentation here.."},
{NULL, NULL}
};
PyMODINIT_FUNC initC_API_Test(void) {
Py_InitModule("C_API_Test", C_API_TestMethods);
}
My Python program works correctly:
from C_API_Test import execMyPyFunc
def myfunc(x):
return 2.0 * x
fx = execMyPyFunc(1.28,myfunc)
print fx
What I would like to do though is to somehow get the pointer from my Python function (PyObject *Fx) and pass this to a C++ function expecting: double(*fx)(double). Does anyone know how to do this (if possible)? I tried to initialize double(*cFx)(double) and cast my Python function as cFx = (double(*)(double))Fx but this does not work. Any ideas?
You aren't going to be able to simply cast a Python function to C like that.
Instead pass the PyObject function pointer, call the function, and convert to C double. This code will return -1 on failure.
static double cFx(PyObject *fx, double x){
PyObject *pyresult = PyObject_CallFunction(fx, "d", x);
if (pyresult == NULL) return -1;
double cppresult = PyFloat_AsDouble(pyresult);
Py_DECREF(pyresult);
if (PyErr_Occurred()) return -1;
return cppresult;
}
Important part of this is to decrement the reference count to the return value of PyObject_CallFunction since you aren't passing it off to the Python interpreter to deal with.

Lua & C++ API getting the executing information

In Lua I have a function called utils.debug() and what I would like to do is use it in my Lua code as follows:
function Foo:doSomething
if (/* something */) then
print("Success!")
else
utils.debug()
end
end
function Foo:doSomethingElse
if (/* something else */) then
print("Awesome!")
else
utils.debug()
end
end
I would like to use it throughout my Lua code to help me debug. As a result, I would like my C++ code to know where in the Lua code the utils.debug() was called from. I looked into lua_Debug and lua_getinfo and they seem pretty close to what I want, but I'm missing a piece:
int MyLua::debug(lua_State* L)
{
lua_Debug ar;
lua_getstack(L, 1, &ar);
lua_getinfo(L, ??????, &ar);
// print out relevant info from 'ar'
// such as in what function it was called, line number, etc
}
Is this what the lua_Debug struct is for or is there another facility or method I should use to do this?
This is what I use to produce a Lua stack trace:
lua_Debug info;
int level = 0;
while (lua_getstack(l, level, &info)) {
lua_getinfo(l, "nSl", &info);
fprintf(stderr, " [%d] %s:%d -- %s [%s]\n",
level, info.short_src, info.currentline,
(info.name ? info.name : "<unknown>"), info.what);
++level;
}
See the documentation for lua_getinfo for more info.

C/C++ embedding python module PIL, the python function want to return all pixel data, but i why can't?

add c/c++ code embedding python. all problem is in c code, I don't known how to get the pixel value.
python code:
import Image
format = ""
mode = ""
size = ""
data = list()
def getImage(file):
im = Image.open(file)
global format
global mode
global size
global data
format = im.format
mode = im.mode
size = im.size
width, height = im.size
for x in range(0, height):
for y in range(0, width):
data.append(im.getpixel((x,y)))
return None
in C/C++ code
My get the data length is 0. Any problems of the two for loop?
void loadImage(char *file) {
Image* img = NULL;
Py_Initialize();
if ( !Py_IsInitialized() ) {
std::cerr<<"Python initalize failed.\n";
return NULL;
}
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append('./')");
PyObject *pName, *pModule, *pFunc, *pArgs;
pName = PyString_FromString("loadImage");
pModule = PyImport_Import(pName);
Py_DECREF(pName);
if ( !pModule ) {
PyErr_Print();
std::cerr<<"import loadImage module faild, please confirm where the file 'loadImage.py' is.\n";
return NULL;
}
pFunc = PyObject_GetAttrString(pModule, "getImage");
if ( !pFunc ) {
PyErr_Print();
std::cerr<<"Can't find method getImage in loadImage module.\n";
return NULL;
}
pArgs = PyTuple_New(1);
PyTuple_SetItem(pArgs, 0, Py_BuildValue("s", file));
PyObject_CallObject(pFunc, pArgs);
PyObject *pFormat, *pMode, *pSize, *pData, *pSeq, *pPixel;
pFormat = PyObject_GetAttrString(pModule, "format");
pMode = PyObject_GetAttrString(pModule, "mode");
pSize = PyObject_GetAttrString(pModule, "size");
if ( !PyTuple_Check(pSize) ) {
std::cerr<<"pSize is not tupple object.\n";
return NULL;
}
pData = PyObject_GetAttrString(pModule, "data");
if ( !pData ) {
std::cerr<<"pData is null.\n";
return NULL;
}
if ( !PyList_Check(pData) ) {
std::cerr<<"pData is not list object.\n";
return NULL;
}
int n = PyList_GET_SIZE(pData);
std::cerr<<n<<"\n";
Py_DECREF(pData);
Py_DECREF(pFunc);
Py_DECREF(pModule);
Py_DECREF(pArgs);
std::cerr<<"Py_DECREF over.\n";
}
I get the lenght of pData(is n) is 0.
Well, your code can literally be replaced with a single line:
data = list(Image.open(file).getdata())
The getdata method of Image objects returns the pixels packed into a list, line-by-line. If you need them in column-major order (as you are doing), apply .transpose with the appropriate arguments first. The result will still be substantially faster and simpler than iterating over all the pixels.
You aren't checking the return value from PyObject_CallObject. It might have returned NULL (Python exception), and consequently failed to modify the data list.
You should check every Python API call you make, since exception handling is critically important to making the interpreter work correctly.
If you do get a NULL, you can call PyErr_Print() to get the traceback printed out to stderr (cerr).
The Python code you run by calling PyObject_CallObject is probably throwing an exception.
NULL means an exception was thrown. You can use various python api functions to get at
the actual error: http://docs.python.org/c-api/exceptions.html. This is probably the simplest.
if( PyObject_CallObject(pFunc, pArgs ) == NULL )
{
// Print the exception (a traceback may or may not be available)
PyErr_Print();
// Do any cleanup...
// Always return NULL after a python api function returns NULL.
return NULL;
}
The problem may caused by the python version 2.7 in my computer. I changed to python 3.2, all code can got ideal return.