Linker error: C/C++ Extensions for python - python

As my title kind of says, I'm trying to develop a C extension for Python. I followed this tutorial here and I ran the setup.py script. How ever when I run the python interpreter and try to import my newly created module, I get a linker error undefined symbol: py_BuildValue. I also tryed to compile it my self and I got the same errors plus an error saying Py_InitModule3 is undefined. I have installed both python3.2-dev and python3-dev. Here is my test.c code:
#include <python3.2/Python.h>
static PyObject* Test(PyObject* self){
return py_BuildValue("s","This is a test and my first trip into the world of python bindings!!");
}
static PyMethodDef test_funcs[] ={{"testExtensions",(PyCFunction)Test, METH_NOARGS,"This is my First Extension!!"},{NULL}};
void initTest(void){
Py_InitModule3("Test", test_funcs, "Extension module example!");
}
And my setup.py code:
from distutils.core import setup, Extension
setup(name='Test', version='1.0', \
ext_modules=[Extension('Test', ['test.c'])])

That's because the function is called Py_BuildValue rather than py_BuildValue. C is case sensitive. If you check further up in your compile messages, you probably also have a warning there about the function being implicitly declared.

Related

Some builtins modules crash python if the interpreter is embedded in a shared library

I'm on MacOs and I'm trying to embed python inside a c shared library. Disclaimer: there are two installations of python involved with this question. One is my "main" python, so to speak, installed at /Library/Frameworks/Python.framework/Versions/3.10/bin/python3, that will be referred as "main python"; and the other one is a subdirectory of my current working directory containing python source code that i will build and embed, and it will be referred as embedded python.
For clarity, I have reproduced a minimalistic example of my problem that gives the same results.
Consider that the current working directory contains:
python310 (a directory containing python source code)
I configure it using the following command:
./configure --prefix=__path_to_the_directory_containing_python_source__ --exec-prefix=__path_to_the_directory_containing_python_source__ --enable-shared --enable-optimizations --with-lto
and compiled it using make && make altinstall
I have used altinstall because I will use this distribution only inside my application and I didn't want to replace my normal python installation.
test_interpreter.c:
#include "__path_to_the_directory_containing_python_source__/Include/Python.h"
int test() {
Py_SetProgramName(L"__path_to_the_directory_containing_python_source__/python.exe");
Py_Initialize();
// adds the current directory to sys.path to make test_import.py importable
PyObject* sysPath = PySys_GetObject("path"); // borrowed reference
PyObject* cur_dir = PyUnicode_FromString(""); // new reference
PyList_Append(sysPath, cur_dir);
PyRun_SimpleString("import test_import");
Py_DECREF(cur_dir);
Py_Finalize();
return 0;
}
test_import.py:
# will be called by embedded python
print("Inside python")
...
test_interpreter.py
# will be called by main python
import ctypes
lib = ctypes.CDLL("/__path_to_current_directory__/libtest.so")
tp = ctypes.CFUNCTYPE(ctypes.c_int)
test = tp(("test", lib))
test()
Now the problem is that, when test_import.py imports some builtin modules (note it doesn't happen with every module but just some), i get errors like segfault (e.g. when importing ctypes) or abort trap (e.g. when importing sqlite3). The most interesting part is that the errors do not occur if the interpreter is embedded inside an executable instead than a shared library.
So, if I compile test_interpreter.c into libtest.so using: gcc -L__path_to_the_directory_containing_python_source__ -lpython3.10 -shared -install_name __current_directory_path__/libtest.so -o libtest.so test_interpreter.c,
then modify test_import.py to for example
# will be called by embedded python
print("Inside python")
import decimal
and execute python3 test_interpreter.py (using main python; version is still 3.10) i get:
Inside python
Segmentation fault: 11
Other modules that give me the same error message are:
tkinter
ctypes
Also, if it can be usefull, I managed to understand that when importing ctypes inside the embedded interpreter the error occures when the line from _ctypes import Union, Structure, Array (of ctypes' __ init __.py) is executed.
If i modify test_interpreter.py to:
print("Inside python")
import sqlite3
and run the same command i get:
Inside python
Python(86563,0x10a7105c0) malloc: *** error for object 0x101d56360: pointer being freed was not allocated
Python(86563,0x10a7105c0) malloc: *** set a breakpoint in malloc_error_break to debug
Abort trap: 6
Other modules that give me the same error message are:
dbm
distutils
random
json
zoneinfo
base64
csv
calendar
pickle
Note that if I compile test_interpreter.c as an executable (after changing test function name to main) using gcc -L__path_to_the_directory_containing_python_source__ -o test test_interpreter.c, or if I run the python executable (without embedding it) and try to import those module, everything works fine.
Thanks in advance to everyone who will try to understand what's going on.

ImportError when importing pybind11 module compiled with cmake/mingw32-make

Im currently trying to write a c++ extension to python and only have a little bit of c++ code so far ... just to test the workflow/compilation.
#include "conservation.h"
#include <pybind11/pybind11.h>
double calculateMomentum(double mass, double velocity) {
return mass * velocity;
}
PYBIND11_MODULE(conservation, m) {
m.doc() = "Conservation-quantity calculator";
m.def("calculate_momentum", &calculateMomentum, "Returns Momentum of given parameters");
}
Then i create a makefile with the following configuration:
cmake_minimum_required(VERSION 3.4...3.19)
project(Calculation LANGUAGES CXX)
set(pybind11_DIR $CACHE{pybind11_DIR})
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ../extensions)
find_package(pybind11 REQUIRED)
pybind11_add_module(conservation ./src/calculation/conservation.cpp)
Afterwards i can compile the extension without any problem on Windows and Linux (using mingw32-make and make respectively)
When I try importing it in Linux it works without any problems or issues and I can run the calculate_momentum function.
But when I try importing the extension on a Windows machine I get the following Error:
ImportError: DLL load failed while importing conservation: The parameter is incorrect.
I'm not a very experienced user of cmake or pybind11 so it is entirely possible that I am doing something completely wrong and any input would be very much appreciated.

Boost not exposing module to python

I'm using some example code I found on the web to try to set up libboost so I can call into cpp routines with Python code. (I intend to use python to write my UI and cpp for my backend for this application) Boost seems simple enough to use, but it's currently not exposing any functionality.
#include <boost/python.hpp>
char const* greet()
{
return "hello, world";
}
BOOST_PYTHON_MODULE(hello_ext)
{
using namespace boost::python;
def("greet", greet);
}
I compile this using the line g++ -c hello.cpp -I/usr/include/python3.6/
(that last include is necessary because I'm on ubuntu, where g++ doesn't locate python correctly, and I'm too lazy to add it to my path)
import hello_ext
print(hello_ext.greet())
I run this using python3, and I get the following output
File "hello.py", line 1, in <module>
import hello_ext
ModuleNotFoundError: No module named 'hello_ext'
This implies to me that Boost is not properly exposing the C++ functionality I created a module for. What am I missing here? I've already tried exposing the functionality to python in a header file instead of in the cpp file, and that has the same result.
Also, if anyone looking at this post is having issues accessing functionality within their module, but it seems like the module is being exposed, make sure python doesn't already have a default module with the same name which would take precedence over your module.
According to boost::python docs your code should be compiled as a shared library to be used in python:
g++ hello.cpp -I /usr/include/python3.6 -lboost_python-py36 -shared -fPIC -o hello_ext.so
Note that the name of the shared library must be the same as the name of your python module. You also forgot to link your code with boost lib.

Cython: Segmentation Fault Using API Embedding Cython to C

I'm trying to embed Cython code into C following O'reilly Cython book chapter 8. I found this paragraph on Cython's documentation but still don't know what should I do:
If the C code wanting to use these functions is part of more than one shared library or executable, then import_modulename() function needs to be called in each of the shared libraries which use these functions. If you crash with a segmentation fault (SIGSEGV on linux) when calling into one of these api calls, this is likely an indication that the shared library which contains the api call which is generating the segmentation fault does not call the import_modulename() function before the api call which crashes.
I'm running Python 3.4, Cython 0.23 and GCC 5 on OS X. The source code are transcendentals.pyx and main.c:
main.c
#include "transcendentals_api.h"
#include <math.h>
#include <stdio.h>
int main(int argc, char **argv)
{
Py_SetPythonHome(L"/Users/spacegoing/anaconda");
Py_Initialize();
import_transcendentals();
printf("pi**e: %f\n", pow(get_pi(), get_e()));
Py_Finalize();
return 0;
}
transcendentals.pyx
cdef api double get_pi():
return 3.1415926
cdef api double get_e():
print("calling get_e()")
return 2.718281828
I'm compiling those files using setup.py and Makefile:
setup.py:
from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize
setup(
ext_modules=cythonize([
Extension("transcendentals", ["transcendentals.pyx"])
])
)
Makefile
python-config=/Users/spacegoing/anaconda/bin/python3-config
ldflags:=$(shell $(python-config) --ldflags)
cflags:=$(shell $(python-config) --cflags)
a.out: main.c transcendentals.so
gcc-5 $(cflags) $(ldflags) transcendentals.c main.c
transcendentals.so: setup.py transcendentals.pyx
python setup.py build_ext --inplace
cython transcendentals.pyx
clean:
rm -r a.out a.out.dSYM build transcendentals.[ch] transcendentals.so transcendentals_api.h
However, I came to error Segmentation fault: 11. Any idea can help with this? Thanks!
In that Makefile there is
transcendentals.so: setup.py transcendentals.pyx
python setup.py build_ext --inplace
Unless python refers to /Users/spacegoing/anaconda/bin/python3 it should be replaced since the module may be compiled for wrong python version, and cannot thus be loaded.
In main.c there is call import_transcendentals() that does not check the return value i.e. if the import fails or succeeds. In case of failure, get_pi() and get_e() point to invalid memory locations and trying to call them causes a segmentation fault.
Also, the module has to be located somewhere where it can be found. It seems that when embedding, the current directory is not searched for python modules. PYTHONPATH environment variable could be changed to include the directory where transcendentals.so is located.
The following is an altenative way of embedding the code to the C program and sidesteps the import issues since the module code is linked to the executable.
Essentially, a call to PyInit_transcendentals() is missing.
File transcendentals.h will be generated when the cython functions are defined public i.e.
cdef public api double get_pi():
...
cdef public api double get_e():
Your main.c should have the include directives
#include <Python.h>
#include "transcendentals.h"
and then in main
Py_Initialize();
PyInit_transcendentals();
There should be no #include "transcendentals_api.h" and no import_transcendentals()
The first reason is that according to the documentation
However, note that you should include either modulename.h or
modulename_api.h in a given C file, not both, otherwise you may get
conflicting dual definitions.
The second reason is, that since transcendentals.c is linked to the program in
gcc $(cflags) $(ldflags) transcendentals.c main.c
there is no reason to import transcendentals module. The module has to be initialized though, PyInit_transcendentals() does that for Python 3

Create a DLL exposing python code

Can I use cython to create a shared library with exported C functions that have python code as the core? Like wrapping Python with C??
It is to be used in plugins.
tk
Using Cython, you can write function declared as C ones with the cdef keyword (and public... important!), with Python inner code:
yourext.pyx
cdef int public func1(unsigned long l, float f):
print(f) # some python code
Note: in the following is assumed that we are working in the root of drive D:\
Building (setup.py)
from distutils.core import setup
from Cython.Distutils import build_ext
setup(
cmdclass = {'build_ext': build_ext},
name = 'My app',
ext_modules = cythonize("yourext.pyx"),
)
Then run python setup.py build_ext --inplace
After running the setup.py (if you are using distutils), you'll get 2 files of interest:
yourext.h
yourext.c
Looking into the .c will show you that func1 is a C function, in the end.
Those two files are all we need to do the rest.
C main program for testing
// test.c
#include "Python.h"
#include "yourext.h"
main()
{
Py_Initialize(); // start python interpreter
inityourext(); // run module yourext
func1(12, 3.0); // Lets use shared library...
Py_Finalize();
}
As we don't use the extension (.pyd) by itself, we need to make a little trick/hack in the header file to disable the "DLL behavior". Add the following at the beginning of "yourext.h":
#undef DL_IMPORT # Undefines DL_IMPORT macro
#define DL_IMPORT(t) t # Redefines it to do nothing...
__PYX_EXTERN_C DL_IMPORT(int) func1(unsigned long, float);
Compiling "yourext" as a shared library
gcc -shared yourext.c -IC:\Python27\include -LC:\Python27\libs -lpython27 -o libyourext.dll
Then compiling our test program (linking to the DLL)
gcc test.c -IC:\Python27\include -LC:\Python27\libs -LD:\ -lpython27 -lyourext -o test.exe
Finally, run the program
$ test
3.0
This is not obvious, and there is many other ways to achieve the same thing, but this works (have a look to boost::python, ..., other solutions may better fit your needs).
I hope this answers a little bit your question or, at least, gave you an idea...

Categories

Resources