Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Embedded Python in C++
#1
Is there a way to PyImport_Import(PyModule) so that all it's functions or classes are run as directly vs as import?

Sample Python Code (main.py):

class TestClass:
  • def print_hi(self, name):
    print(f"print_hi {name})


if __name__ == '__main__'
  • t = TestClass()
    t.print_hi("test")

in c++ one can import the main.py with:
  • PyImport_Import(pyMain)
and created the class instance with:
  • PyObject_callObject(pyTestClass)

How do one put the class instance behind the same guard as one can in python
if __name__ == '__main__'
  • classs.function...

Right now in all the module imported to c world is running as import, not direct.
Is there way to switch in c world?
Gribouillis write Jul-30-2022, 07:30 AM:
Please post all code, output and errors (it it's entirety) between their respective tags. Refer to BBCode help topic on how to post. Use the "Preview Post" button to make sure the code is presented as you expect before hitting the "Post Reply/Thread" button.
Reply
#2
(Jul-30-2022, 05:12 AM)Xeno Wrote: How do one put the class instance behind the same guard as one can in python
It is not clear what you want to do. In Python, the if __name__ == '__main__' only means that the following code must be executed only if the current module's name is '__main__'. There is no obstacle to do the same in C++, the code can check the current module's name.

Or perhaps you want to execute a Python files containing if __name__ == '__main__' sections and you want these sections to be executed, which won't work with an import statement. In that case, you could probably use PyRun_File() to run the file in a dictionary where '__name__' --> '__main__'.
Reply
#3
   

I am mainly testing embedding python class in c++, just want to find a way to initilzie the python class that is behined if __name__ == '__main__'

I am not sure how PyRun_File() will help me here, can you show me know how to import a python class with it, and then call functions in the python class?
Gribouillis write Jul-30-2022, 03:39 PM:
Please post all code, output and errors (it it's entirety) between their respective tags. Refer to BBCode help topic on how to post. Use the "Preview Post" button to make sure the code is presented as you expect before hitting the "Post Reply/Thread" button.
Reply
#4
Here is a (quick and dirty) simple example in C created from the very high level embedding basic example.
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <stdio.h>

int
main(int argc, char *argv[])
{
    wchar_t *program = Py_DecodeLocale(argv[0], NULL);
    const char *spamclass = R""""(
class Spam:
    def func(self):
        if __name__ == '__main__':
            print('called in __main__')
        else:
            print('called out of __main__')
    )"""";
    PyObject *mainmod, *Spam, *spam, *meth;
    if (program == NULL) {
        fprintf(stderr, "Fatal error: cannot decode argv[0]\n");
        exit(1);
    }
    Py_SetProgramName(program);  /* optional but recommended */
    Py_Initialize();
    PyRun_SimpleString("from time import time,ctime\n"
                       "print('Today is', ctime(time()))\n");
    PyRun_SimpleString(
        spamclass
    );
    mainmod = PyImport_ImportModule("__main__");
    Spam = PyObject_GetAttrString(mainmod, "Spam");
    spam = PyObject_CallObject(Spam, NULL);
    meth = PyObject_GetAttrString(spam, "func");
    PyObject_CallObject(meth, NULL);
//     printf("%p\n", spam);
    if (Py_FinalizeEx() < 0) {
        exit(120);
    }
    PyMem_RawFree(program);
    return 0;
}
Here is the output
Output:
Today is Sat Jul 30 17:56:09 2022 called in __main__
and the makefile
foo.o: foo.c
	gcc -c foo.c -I/usr/include/python3.8 -I/usr/include/python3.8  -Wno-unused-result -Wsign-compare -g -fdebug-prefix-map=/build/python3.8-uvizni/python3.8-3.8.10=. -specs=/usr/share/dpkg/no-pie-compile.specs -fstack-protector -Wformat -Werror=format-security  -DNDEBUG -g -fwrapv -O3 -Wall -fPIE

a.out: foo.o
	 gcc foo.o -L/usr/lib/python3.8/config-3.8-x86_64-linux-gnu -L/usr/lib  -lcrypt -lpthread -ldl  -lutil -lm -lm -lpython3.8
Reply
#5
oh cool, thank you so much for the sample, I will use this to test my wrapper now.

this is really help fully.
Reply
#6
I do have a follow up question on the return values

when I import __main__ and then do PyRun all is working good

but now it seems I can no long decode the python return value

class Spam:
    def func(self):
        if __name__ == '__main__':
            print('called in __main__')
            return "Completed"
        else:
            print('called out of __main__')
            return "Skipped"
    )"""";
I use to be able to decode the return values on the c++ side like this

            PyObject* result= PyObject_CallObject(meth, NULL);
            PyObject* repr = PyObject_Repr(result);
            PyObject* str = PyUnicode_AsEncodedString(repr, "utf-8", "~I~");
            cout << PyBytes_AS_STRING(str) << endl;
but now str object is NULL?
Reply
#7
(Jul-31-2022, 07:26 AM)Xeno Wrote: but now str object is NULL?
Could you post complete C++ code to reproduce the issue? (preferably with makefile)
Reply
#8
I was using the sample code as base and trying to return a string from the func.

mainly it seem to fail on:
str = PyUnicode_AsEncodedString(result, "utf-8", "~I~");
the returned str object is NULL

int main() {
        const char* spamclass = R""""(
class Spam:
    def func(self):
        if __name__ == '__main__':
            print('called in __main__')
            return "Completed"
        else:
            print('called out of __main__')
            return "Skipped"
    )"""";
        PyObject* mainmod, * Spam, * spam, * meth, * result, * repr, *str;
        const char* s;
        Py_Initialize();
        PyRun_SimpleString("from time import time,ctime\n"
            "print('Today is', ctime(time()))\n");
        PyRun_SimpleString(
            spamclass
        );
        mainmod = PyImport_ImportModule("__main__");
        Spam = PyObject_GetAttrString(mainmod, "Spam");
        spam = PyObject_CallObject(Spam, NULL);
        meth = PyObject_GetAttrString(spam, "func");
        result = PyObject_CallObject(meth, NULL);
        if (!PyUnicode_CheckExact(result)) {
            repr = PyObject_Repr(result);
            str = PyUnicode_AsEncodedString(repr, "utf-8", "~I~");
            s = PyBytes_AS_STRING(str);
            cout << "response from python: " << s << endl;
        }
        else {
            str = PyUnicode_AsEncodedString(result, "utf-8", "~I~");
            s = PyBytes_AS_STRING(str);
            cout << "response from python: " << s << endl;
        }
        if (Py_FinalizeEx() < 0) {
            exit(120);
        }
        return 0;
}
Reply
#9
It works for me. I could not reproduce the bug.
Output:
Today is Sun Jul 31 22:52:54 2022 called in __main__ response from python: Completed
Makefile:
Output:
# foo.o must come before the libraries when linking a.out: foo.o g++ foo.o -o a.out -L/usr/lib/python3.8/config-3.8-x86_64-linux-gnu -L/usr/lib -lpython3.8 -lcrypt -lpthread -ldl -lutil -lm -lm foo.o: foo.cpp g++ foo.cpp -c -I/usr/include/python3.8 -I/usr/include/python3.8 -Wno-unused-result -Wsign-compare -g -fdebug-prefix-map=/build/python3.8-uvizni/python3.8-3.8.10=. -specs=/usr/share/dpkg/no-pie-compile.specs -fstack-protector -Wformat -Werror=format-security -DNDEBUG -g -fwrapv -O3 -Wall -fpie clean: rm -f a.out foo.o
Full C++ code
#define PY_SSIZE_T_CLEAN
#include "Python.h"
#include <iostream>
using namespace std;

int main() {
        const char* spamclass = R""""(
class Spam:
    def func(self):
        if __name__ == '__main__':
            print('called in __main__')
            return "Completed"
        else:
            print('called out of __main__')
            return "Skipped"
    )"""";
        PyObject* mainmod, * Spam, * spam, * meth, * result, * repr, *str;
        const char* s;
        Py_Initialize();
        PyRun_SimpleString("from time import time,ctime\n"
            "print('Today is', ctime(time()))\n");
        PyRun_SimpleString(
            spamclass
        );
        mainmod = PyImport_ImportModule("__main__");
        Spam = PyObject_GetAttrString(mainmod, "Spam");
        spam = PyObject_CallObject(Spam, NULL);
        meth = PyObject_GetAttrString(spam, "func");
        result = PyObject_CallObject(meth, NULL);
        if (!PyUnicode_CheckExact(result)) {
            repr = PyObject_Repr(result);
            str = PyUnicode_AsEncodedString(repr, "utf-8", "~I~");
            s = PyBytes_AS_STRING(str);
            cout << "response from python: " << s << endl;
        }
        else {
            str = PyUnicode_AsEncodedString(result, "utf-8", "~I~");
            s = PyBytes_AS_STRING(str);
            cout << "response from python: " << s << endl;
        }
        if (Py_FinalizeEx() < 0) {
            exit(120);
        }
        return 0;
}
Reply
#10
that is wired, the 2 diffrence I can see is

I am running this in vs2019, and running with python 3.10

is there anything linker wise i need to add in vc++?

this is the C++ options
/JMC /permissive- /ifcOutput "x64\Debug\" /GS /W3 /Zc:wchar_t /I"C:\Program Files\Python310\include" /ZI /Gm- /Od /sdl /Fd"x64\Debug\vc142.pdb" /Zc:inline /fp:precise /D "_DEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /errorReport:prompt /WX- /Zc:forScope /RTC1 /Gd /MDd /FC /Fa"x64\Debug\" /EHsc /nologo /Fo"x64\Debug\" /Fp"x64\Debug\cppTester.pch" /diagnostics:column

Linker options
/OUT:"C:\Users\WINSIM\PycharmProjects\testProject\cpp\cppTester\x64\Debug\cppTester.exe" /MANIFEST /NXCOMPAT /PDB:"C:\Users\WINSIM\PycharmProjects\testProject\cpp\cppTester\x64\Debug\cppTester.pdb" /DYNAMICBASE "kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib" "comdlg32.lib" "advapi32.lib" "shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib" "odbc32.lib" "odbccp32.lib" /DEBUG /MACHINE:X64 /INCREMENTAL /PGD:"C:\Users\WINSIM\PycharmProjects\testProject\cpp\cppTester\x64\Debug\cppTester.pgd" /SUBSYSTEM:CONSOLE /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /ManifestFile:"x64\Debug\cppTester.exe.intermediate.manifest" /LTCGOUT:"x64\Debug\cppTester.iobj" /ERRORREPORT:PROMPT /ILK:"x64\Debug\cppTester.ilk" /NOLOGO /LIBPATH:"C:\Users\WINSIM\PycharmProjects\testProject\cpp\cppTester\x64\Debug" /LIBPATH:"C:\Program Files\Python310\libs" /TLBID:1
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  How to run detectron2, as python embedded code in C++, on GPU? hassaniqbal931 3 1,115 Nov-02-2023, 04:45 PM
Last Post: blabling2
  Adding libraries to embedded Python as a ZIP The_Oman 0 1,247 May-05-2023, 04:05 PM
Last Post: The_Oman
Bug Embedded Python Memory Leaks Alexei 1 1,036 Sep-16-2022, 01:15 PM
Last Post: Alexei
  Embedded python fails to compile on Raspberry Pi tryfon 2 3,494 Dec-22-2020, 02:06 PM
Last Post: tryfon
  Can Embedded Python run any shared library on Android ? sprotz 0 2,334 Nov-08-2020, 12:21 PM
Last Post: sprotz
  memory leak on embedded python in c++ asdf3721 3 3,408 Jul-16-2020, 06:33 AM
Last Post: Gribouillis
  Embedded Python PyObject_CallObject function JRHeisey 1 2,400 Nov-27-2019, 01:50 AM
Last Post: casevh
  Trying to implement Python into embedded OS thesurya7 2 2,417 Apr-02-2019, 06:38 PM
Last Post: ebolisa
  Multiple calls to Python interpreter embedded in C++ application yield segmentation f mmoelle1 0 2,843 Mar-21-2019, 08:54 PM
Last Post: mmoelle1
  python code (embedded in Redshift) to return result of the query Mel 0 2,462 Aug-24-2018, 06:12 PM
Last Post: Mel

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020