Passing SWIG objects from C++ to Python gives 'SwigPyObject' object has no attribute <method>

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

Passing SWIG objects from C++ to Python gives 'SwigPyObject' object has no attribute <method>

Thor List
Hi,

I have a C++ project in Windows with many objects. I have used SWIG 3.0.10
successfully to expose a few of them and generate the PY and PYD files and
can create and work with the objects in Python. However, when I try to pass
the same type of object from C++ to Python I get the 'SwigPyObject' object
has no attribute <method> error.

My interface file is:

%module cmsdk
%include "std_string.i"
%include "std_map.i"
%include "std_vector.i"
%{
#include "DataMessage.h"
#include "Subscriptions.h"
#include "PsySpace.h"
#include "PsyAPI.h"
using namespace cmlabs;
%}
%include "include/DataMessage.h"
%include "include/Subscriptions.h"
%include "include/PsyAPI.h"

I create the cmsdk.py and _cmsdk.pyd files and put them into the root of D:\
along with all other .py files.

The following works fine in standalone Python:

import cmsdk
p1 = cmsdk.PsyAPI(None)
size = p1.getInputQueueSize()
print("Queue size: %u" % size)

However, when I run the following code in C++ to call out to Python passing
the PsyAPI object:

Py_Initialize();
int ret = PyRun_SimpleString("import sys");
ret = PyRun_SimpleString("sys.path.append(\"D:/\")");
ret = PyRun_SimpleString("import cmsdk");
pName = PyUnicode_DecodeFSDefault("mypythonfile");
pModule = PyImport_Import(pName);
Py_DECREF(pName);
if (pModule != NULL) {
        pFunc = PyObject_GetAttrString(pModule, "PsyCrank");
        if (pFunc && PyCallable_Check(pFunc)) {
        PyObject *argobj = SWIG_NewPointerObj((void*)api,
SWIGTYPE_p_cmlabs__PsyAPI, 0);
        try {
                PyObject *arglist = Py_BuildValue("(O)", argobj);
// Build argument list
                PyObject_CallObject(pFunc, arglist);     // Call Python
        }
        catch (...) {
        }
}

with mypythonfile.py containing:

import cmsdk
def PsyCrank(api):
    print("Hello World")
    try:
        a = cmsdk.PsyAPI(None);
        size = a.getInputQueueSize()
        print("Queue1 size: %u" % size)
        size = api.getInputQueueSize()
        print("Queue2 size: %u" % size)
    except Exception:
        print("Exception handled")
    return

the code prints the "Hello World" and the "Queue1 size" lines, but fails
when calling api.getInputQueueSize() with

size = api.getInputQueueSize()
AttributeError: 'SwigPyObject' object has no attribute 'getInputQueueSize'

It looks like the PsyAPI object api doesn't get into Python correctly.

Can anyone tell me what I am doing wrong, please?

Thanks,
Thor




------------------------------------------------------------------------------
_______________________________________________
Swig-user mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/swig-user
Reply | Threaded
Open this post in threaded view
|

Re: Passing SWIG objects from C++ to Python gives 'SwigPyObject' object has no attribute <method>

Thor List
OK, I have created a complete, minimal and standalone example (download link
included below).

I have a C++ object Color in a static library:

    class Color
    {
    public:
    Color(int red, int green, int blue);
    void printMe();
    int r, g, b;
    };

    Color::Color(int red, int green, int blue) {
    r = red; g = green; b = blue;
    }

    void Color::printMe() {
    std::cout << "(" << r << "," << g << "," << b << ")" << std::endl;
    }

For this I created an interface file for Python:

    %module color
    %{
    #include "color.h"
    %}
    %include "color.h"

which when compiled and linked creates the following files:

    color_wrap.cxx
    color.py
    _color.pyd

I can now run the following Python program successfully:

    import color
    otherColor = color.Color(20,20,20)
    otherColor.printMe()

Now I want to create the Color object in a C++ program and pass a pointer to
it to a Python program.

In C++ I have my main() function:

    #include "color_wrap.cxx"
    #include "color.h"
   
    int main() {
            cmlabs::Color* color = new cmlabs::Color(10,10,10);
            color->printMe();
   
            PyObject *pModule, *pFunc;
            if (!Py_IsInitialized())
                    Py_Initialize();
   
            int ret = PyRun_SimpleString("import sys");
            if (ret != 0) {
                    printf("Error running commands in Python\n");
                    return -1;
            }
   
            ret = PyRun_SimpleString("sys.path.append(\"d:\\swig\")");
            if (ret != 0) {
                    printf("Error setting path in Python\n");
                    return -1;
            }
   
            pModule = PyImport_ImportModule("colorapp");
            if (pModule == NULL) {
                    printf("Couldn't load python script file colorapp.py in
script path d:\\swig");
                    return -1;
            }
   
            if (pModule != NULL) {
                    pFunc = PyObject_GetAttrString(pModule, "ColorTest");
                    /* pFunc is a new reference */
   
                    if (pFunc && PyCallable_Check(pFunc)) {
   
                            PyObject *argobj =
SWIG_NewPointerObj((void*)color, SWIGTYPE_p_cmlabs__Color, 0);
                            try {
                                    PyObject *arglist = Py_BuildValue("(O)",
argobj);   // Build argument list
                                    PyObject_CallObject(pFunc, arglist);
// Call Python
                                    if (PyErr_Occurred())
                                    {
                                            PyErr_Print();
                                            PyErr_Clear();
                                    }
                                    Py_DECREF(arglist);
                            }
                            catch (...) {
                                    printf("Exception running function\n");
                            }
                            Py_DECREF(argobj);
                    }
                    Py_DECREF(pFunc);
                    Py_DECREF(pModule);
            }
   
            Py_Finalize();
   
            return 0;
    }

And I create a new python code file to be called from the C++ program:

    import color
    def ColorTest(col):
        otherColor = color.Color(20,20,20)
        otherColor.printMe()
        col.printMe()

The creation of a new object still works fine, but when using the Color
object which was passed from C++ to Python I get this error:

Traceback (most recent call last):
  File "d:\swig\colorapp.py", line 6, in ColorTest
    col.printMe()
AttributeError: 'int' object has no attribute 'printMe'

The project can be downloaded from http://download.cmlabs.com/swigcolor.zip
- open in VS2016 on a 64-bit Windows and compile for Release 64-bit (other
configs not configured correctly). Once compiled copy the ouput files
(color.py, _color.pyd, ColorApp.exe) and the python files (colorapp.py,
colortest.py) to d:\swig. This location can be changed in the ColorApp.cpp,
line 29.

Python obviously knows about the Color object, but does not realise that the
object provided in the function parameter is a Color object.

If I in Python ask about the object types

    print (type(otherColor))
    print (type(col))

I get the following output

    class 'color.Color'
    class 'SwigPyObject'

How do I get Python to realise that the passed argument is of type Color?

Thank you for any help you can provide!
Thor



------------------------------------------------------------------------------
_______________________________________________
Swig-user mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/swig-user