SWIG : AttributeError: 'module' object has no attribute 'fact' - python

I am studying how to use swig to make a C expansion for my python code.And I use the code I get from website as example.
Here is my code:
example.c
#include <time.h>
double My_variable = 3.0;
int fact(int n) {
if (n <= 1) return 1;
else return n*fact(n-1);
}
int my_mod(int x, int y) {
return (x%y);
}
example.h
#ifndef EXAMPLE_H_
#define EXAMPLE_H_
extern double My_variable;
extern int fact(int n);
extern int my_mod(int x, int y);
#endif
example.i
%module example
%{
/* Put header files here or function declarations like below */
#define SWIG_FILE_WITH_INIT
#include "example.h"
%}
%#include "example.h"
Makefile
all:
rm -f *.so *.o *_wrap.* *.pyc
swig -python example.i
gcc -c -fPIC example_wrap.c -I/usr/include/python2.7
gcc -shared example_wrap.o -o _example.so
clean:
rm -f *.so *.o *_wrap.* *.pyc
test.py
import example
print str(example.fact(2))
The test.py is used to check if the expansion works.But when I run the test.py , it output:
Traceback (most recent call last):
File "test.py", line 3, in <module>
print str(example.fact(2))
AttributeError: 'module' object has no attribute 'fact'
Here is the output when I use dir(example):
['__builtins__', '__doc__', '__file__', '__name__', '__package__', '_example', '_newclass', '_object', '_swig_getattr', '_swig_property', '_swig_repr', '_swig_setattr', '_swig_setattr_nondynamic']
What's the reason of this output appears?
If I want to the programme run successfully,how should I do?

Please try to replace as below:
%#include "example.h"
by
%include "example.h"

Reference
Try changing the Makefile to
%module example
%{
#include "example.h"
%}
int fact(int n);
I've assumed you have only one method to export

Related

How to wrap C++ VTK custom function for Python

Is there any place I can find a working example to wrap a C++ function (using VTK) to be used by
Python?
Searched and found this page
mix VTK and SWIG Python
but there is no compiling details, and this simple example on this page
https://public.kitware.com/pipermail/vtkusers/2002-October/013980.html
But for my system Ubuntu 16.04, vtk 8.2.0, Python 3.6.8, I could not make the example work.
I have to modify them to let the compiling done without error.
Details:
#============simple.cpp===================
// simple.c
//based on https://public.kitware.com/pipermail/vtkusers/2002-October/013980.html
#include "Python.h"
#include "vtkPythonUtil.h"
#include "vtkObject.h"
#include "vtkObjectBase.h"
#include <stdio.h>
#include "vtkRenderer.h"
extern "C"
{
static PyObject* simple_vtk_function(PyObject *self, PyObject *args);
}
static PyObject* simple_vtk_function(PyObject *self, PyObject *args)
{
PyObject *vtkpy;
if (!PyArg_ParseTuple(args, "O", &vtkpy))
return NULL;
vtkObjectBase *obj = vtkPythonUtil::GetPointerFromObject(vtkpy,"PyObject");
/* Do what you want to do here. */
//printf("%s\n", obj->GetClassName());
printf("inside module simple_vtk, function simple_vtk_function\n");
Py_RETURN_NONE;
}
static char simple_docs[] =
"simple( ): Any message you want to put here!!\n";
static PyMethodDef module_methods[] =
{
{
"simple_vtk_function", (PyCFunction) simple_vtk_function,
METH_VARARGS, simple_docs
},
{NULL}
};
static struct PyModuleDef simple_vtk =
{
PyModuleDef_HEAD_INIT,
"simple_vtk",/* name of module */
"",/* module documentation, may be NULL */
-1,
module_methods
};
PyMODINIT_FUNC PyInit_simple_vtk(void)
{
return PyModule_Create(&simple_vtk);
}
#============setup.py===================
#!/usr/bin/env python3
import distutils.core as dc
module = dc.Extension('simple_vtk',
sources = ['simple.cpp'],
include_dirs=['/usr/local/include/vtk-8.2'],
libraries_dirs = ['/usr/local/include/vtk-8.2'],
#libraries=['vtkPythonUtil']
)
dc.setup(name = 'simple_vtk',
version = '1.0',
ext_modules = [module])
compile command
CFLAGS="-std=c++11" python setup.py build
and get this printing out
/home/jun/anaconda3/lib/python3.6/distutils/extension.py:131: UserWarning: Unknown Extension options: 'libraries_dirs'
warnings.warn(msg)
running build
running build_ext
building 'simple_vtk' extension
gcc -pthread -B /home/jun/anaconda3/compiler_compat -Wl,--sysroot=/ -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -std=c++11 -fPIC -I/usr/local/include/vtk-8.2 -I/home/jun/anaconda3/include/python3.6m -c simple.cpp -o build/temp.linux-x86_64-3.6/simple.o
cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++
simple.cpp: In function ‘PyObject* simple_vtk_function(PyObject*, PyObject*)’:
simple.cpp:22:19: warning: unused variable ‘obj’ [-Wunused-variable]
vtkObjectBase *obj = vtkPythonUtil::GetPointerFromObject(vtkpy,"PyObject");
^
g++ -pthread -shared -B /home/jun/anaconda3/compiler_compat -L/home/jun/anaconda3/lib -Wl,-rpath=/home/jun/anaconda3/lib -Wl,--no-as-needed -Wl,--sysroot=/ -std=c++11 build/temp.linux-x86_64-3.6/simple.o -o build/lib.linux-x86_64-3.6/simple_vtk.cpython-36m-x86_64-linux-gnu.so
to test in python
>>> import simple_vtk
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: /home/jun/project/play/swig/for_vtk_v2/simple_vtk.cpython-36m-x86_64-linux-gnu.so: undefined symbol: _ZN13vtkPythonUtil20GetPointerFromObjectEP7_objectPKc
The C++ function is so simple, it just to convert a PyObject and convert to vtkObjectBase. I didn't even remove the simple print
printf("%s\n", obj->GetClassName());
But I could never make it work. I think it is a common need for people to wrap a custom C++ vtk function for Python, a little bit surpring there is almost no woring example on internet.
Could you please help me on this? Thanks!

iterate returned vector<pair<int,int>> in python from SWIG bindings

I found this very helpful question and answer:
Return vector<pair<int,int>> & from c++ method to python list of tuples using swig typemap
However I have some problems iterating the return vector if it's not a reference, here the example:
myclass.h:
#include <vector>
#include <utility>
using std::vector;
using std::pair;
class MyClass {
private:
vector<pair<int,int> > _myvector;
public:
MyClass( );
const vector<pair<int,int> > & GetMyVector() const;
};
myclass.cpp:
#include "myclass.h"
MyClass::MyClass(): _myvector()
{_myvector.push_back(std::make_pair(1,2));_myvector.push_back(std::make_pair(1,2));};
const vector<pair<int,int>> & MyClass::GetMyVector() const {
return _myvector;
};
myclass.i:
%module x
%include <std_pair.i>
%include <std_vector.i>
%include <std_string.i>
%template() std::pair<int,int>;
%template(PairVector) std::vector<std::pair<int,int> >;
%{
#include "myclass.h"
%}
%include "myclass.h"
compiled with:
g++ -std=c++11 -c -fPIC myclass.cpp
swig -c++ -v -python myclass.i
g++ -std=c++11 -fPIC -c myclass.cpp myclass_wrap.cxx -I/usr/include/python2.7
g++ myclass.o myclass_wrap.o -shared -fPIC -o _x.so
but when I run something like this in python:
import x
b=x.MyClass()
print(b.GetMyVector())
for a,b in b.GetMyVector():
print(a,b)
then I get:
<Swig Object of type 'vector< std::pair< int,int >,std::allocator< std::pair< int,int > > > *' at 0x7ff06804b1b0>
Traceback (most recent call last):
File "Test.py", line 6, in <module>
for a,b in b.GetMyVector():
TypeError: 'SwigPyObject' object is not iterable
How can I iterate the returned vector properly in python? And why is a pointer to vector returned? Do I have to change something in the swig file?
In case it is relevant: (on Ubuntu)
SWIG Version 2.0.11
g++ (Ubuntu 4.9.4-2ubuntu1~14.04.1) 4.9.4
Python 2.7.6
SWIG doesn't understand the using directive correctly.
Same as in this question and answer:
SWIG argument error when using "using std::vector" in python
As to why a pointer is returned, well, if SWIG cannot convert a returned object into a python object, then it wraps a pointer to the object.

SWIG Python binding seems empty

I have the following files for generating a Python binding to a C++ project with SWIG and CMake:
test.h
int add(int a, int b);
test.cpp
int add(int a, int b)
{
return a+b;
}
test.i
%module test
%{
#include "test.h"
%}
CMakeLists.txt
cmake_minimum_required(VERSION 2.6)
project(swig-test)
# This is a CMake example for Python
add_library(testcpp SHARED test.cpp)
FIND_PACKAGE(SWIG REQUIRED)
INCLUDE(${SWIG_USE_FILE})
FIND_PACKAGE(PythonLibs)
INCLUDE_DIRECTORIES(${PYTHON_INCLUDE_PATH})
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
SET(CMAKE_SWIG_FLAGS "")
SET_SOURCE_FILES_PROPERTIES(test.i PROPERTIES CPLUSPLUS ON)
SET_SOURCE_FILES_PROPERTIES(test.i PROPERTIES SWIG_FLAGS "-includeall")
set(${CMAKE_CXX_FLAGS} "${CMAKE_CXX_FLAGS} -fPIC")
SWIG_ADD_MODULE(test python test.i)
SWIG_LINK_LIBRARIES(test testcpp)
It compiles successfully and libtestcpp.so, _test.so, and test.py are created. strings libtestcpp.so and strings _test.so both have an entry _Z3addii and import test works in Python, but there is nothing under the test namespace in Python.
I've also tried manually compiling with
swig -c++ -python test.i
g++ -c -fpic test.cpp test_wrap.cxx -I/usr/include/python2.7 -I.
g++ -shared test.o test_wrap.o -o _test.so
with the same result.
It may be useful to note that import test isn't a completely empty module; import test; dir(test) yields
['__builtin__',
'__builtins__',
'__doc__',
'__file__',
'__name__',
'__package__',
'_newclass',
'_object',
'_swig_getattr',
'_swig_property',
'_swig_repr',
'_swig_setattr',
'_swig_setattr_nondynamic',
'_test']
and import test; help(test) has a description that it is created by SWIG.
You need to to add a line %include "test.h" at the end in test.i
%module test
%{
#include "test.h"
%}
%include "test.h"

SWIG Python undefined symbol error

I'm trying to create a *.so file for further use in Python using SWIG, but something isn't working.
I have two files:
DataGatherer.h
#include <iostream>
#include <stdlib.h>
#include <time.h>
#include "gnublin.h"
#include <pthread.h>
class dataGatherer
{
private:
int threshold;
int timeThreshold;
int data[4096];
bool running;
gnublin_spi* spiDevice;
pthread_t spiThread;
void *params;
public:
dataGatherer(void);
dataGatherer(int, int);
void initData();
int getThreshold(void);
int* getData(void);
int getPeak(void);
void initSPI(void);
void gatherData();
void * run(void * arg);
void stop(void);
// for use of thread we have to implement some methods from C
static void * start_static(void * params)
{
dataGatherer * thread_this = static_cast<dataGatherer*>(params);
return thread_this->run(thread_this->params);
}
void start(void * params)
{
this->params = params;
pthread_create(&spiThread, 0, &dataGatherer::start_static, this);
}
};
and spiController.h
#include "dataGatherer.h"
class spiController
{
private:
bool runGather;
dataGatherer* gatherer;
int data[4096];
public:
spiController(void);
spiController(int, int);
void initData();
bool checkStop();
void stop();
void start();
};
My spiController.i interface file looks like this:
/* spiController.i */
%module spiController
%{
#include "dataGatherer.h"
#include "spiController.h"
#include "gnublin.h"
#include <stdlib.h>
#include <time.h>
#include <pthread.h>
extern void initData();
extern bool checkStop();
extern void stop();
extern void start();
%}
extern void initData();
extern bool checkStop();
extern void stop();
extern void start();
At the end I try to create the *.so file using commands in the terminal like in the example on the SWIG page with:
swig -python -c++ spiController.i
c++ -c spiController_wrap.c -I/usr/include/python2.7
c++ -shared spiController_wrap.o -o _spiController.so
*.cxx, *.o and *.so file are created with no error, but when I import the spiController into the python code I get:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "spiController.py", line 26, in <module>
_spiController = swig_import_helper()
File "spiController.py", line 22, in swig_import_helper
_mod = imp.load_module('_spiController', fp, pathname, description)
ImportError: ./_spiController.so: undefined symbol: _Z9checkStopv
It's my first try using SWIG and I'm already stuck at this point. How can I resolve this?
I just got the same error and finally figured out why. As above people said, when it says unfound symbol like yours and gives the undefined function name '_Z9checkStopv', always check for the implementation of this function in .cpp file as well as any function declaration of the same name!!
For my case, my cpp does define my 'unfound symbol' constructor function, but in my .h file, i have an overloaded operator= (for the constructor) which is undefined in .cpp file. So swig wraps both default constructor(implemented in .cpp) and operator= (not implemented). Therefore when import, this unimplemented operator= produces the error. Hope this helps!
You must link the library that defines your C++ functions that you have declared like checkStop etc. You would add -L<path to your C++ DLL> -l<name of your C++ DLL> on 3rd line of your example's compile steps.
Like this:
c++ -L<path to DLL> -l<name of your dll> -shared spiController_wrap.o -o _spiController.so
Just as Adam's comment and my experience, you should first compile your XXX.cpp file into XXX.o, the whole command lines maybe like following:
swig -python -c++ XXX.i
g++ -c -fpic XXX.cpp* (this command will generate XXX.o file)
g++ -c -fpic XXX_wrap.cxx -I/usr/include/python2.7* (this command will generate XXX_wrap.o file)
g++ -shared XXX.o XXX_wrap.o -o XXX.so
Although there may be many causes for this problem, I got the exact same error when I compiled the shared library with the python v3.5 headers, e.g.
swig -python example.i
gcc -fPIC -c example.c example_wrap.c -I/usr/include/python3.5 # <-- ISSUE HERE
gcc -shared example.o example_wrap.o -o _example.so
But then later tried to use the example library using python test.py, which ran python v2.7 on system (so it was a python version mismatch issue).
In my case I was also getting that error, and spent some time trying out stuff without success. My problem was that although my source file was plain C I had it named with the .cpp extension, assuming it wouldn't matter. Changing the extension to .c solved automatically the issue.
Another way of solving it was to add the line #include "example.cpp" to the header section of SWIG's .i file.
So, summarizing:
example.c
int fact(int n) {
if (n <= 1) return 1;
else return n*fact(n-1);
}
example.i
%module example
%{
extern int fact(int n);
%}
extern int fact(int n);
Then the following worked for me (in Ubuntu 17.10):
swig -c++ -python example.i
gcc -fPIC -c example.c example_wrap.c -I /usr/include/python2.7
gcc -shared example.o example_wrap.o -o _example.so
python -c "import example; print example.fact(5)"
Hope this helps someone!
Cheers
I know the solution
At the end of make up the share,you should usr g++ -shared -fpic *.o *.o -o _***.so

Linking problem as I am making python module using OpenSSL + SWIG

I have a C file dtls_udp_echo.c in which I use SSL functions. I am trying to create a Python wrapper for this file using SWIG. I've done the following steps:
1) Created an interface file udp.i:
%module udp
%{
/* Put header files here or function declarations like below */
#define SWIG_FILE_WITH_INIT
#include "dtls_udp_echo.h"
%}
int THREAD_setup();
int THREAD_cleanup();
int handle_socket_error();
int generate_cookie(SSL *ssl, unsigned char *cookie, unsigned int *cookie_len);
int verify_cookie(SSL *ssl, unsigned char *cookie, unsigned int cookie_len);
int dtls_verify_callback (int ok, X509_STORE_CTX *ctx) ;
void* connection_handle(void *info);
void start_server(int port, char *local_address);
void start_client(char *remote_address, char *local_address, int port, int length, int messagenumber);
2) Run the command swig -python udp.i.
3) Run the command gcc -O2 -fPIC -c dtls_udp_echo.c -I/usr/local/ssl/include -L/usr/local/ssl/lib -lcrypto -lssl. The path to the include and library is correct, I checked it!
4) Run the command gcc -O2 -fPIC -c udp_wrap.c -I/usr/include/python2.5 -I/usr/local/ssl/include -L/usr/local/ssl/lib -lcrypto -lssl.
5) Run the command gcc -shared dtls_udp_echo.o udp_wrap.o -o _udp.so.
It seems to complete OK as no errors are reported. But, when I try to import the module, I get the following traceback:
>>> import udp
> Traceback (most recent call last):
> File "<stdin>", line 1, in <module>
> File "udp.py", line 28, in <module>
> import _udp ImportError: ./_udp.so: undefined symbol:
> SSL_get_rbio
Can anybody help me to fix this problem?
It can't find the OpenSSL library. Add it to your ld search path; see the ldconfig(8) man page for details.

Categories