14

SPOILER: partially solved (see at the end).

Here is an example of code using Python embedded:

#include <Python.h>
int main(int argc, char** argv)
{
    Py_SetPythonHome(argv[1]);
    Py_Initialize();
    PyRun_SimpleString("print \"Hello !\"");
    Py_Finalize();
    return 0;
}

I work under Linux openSUSE 42.2 with gcc 4.8.5 (but I also have the same problem on openSUSE 13.2 with gcc 4.8.3 or RedHat 6.4 with gcc 4.4.7).

I compiled a static and a dynamic version of Python 2.7.9 (but I also have the same problem with Python 2.7.13).

I compile my example linking to the static version of Python with the following command:

g++ hello.cpp -o hello \
-I /home/caduchon/softs/python/2.7.9/64/gcc/4.8.5/static/include/python2.7 \
-L /home/caduchon/softs/python/2.7.9/64/gcc/4.8.5/static/lib \
-l python2.7 -l pthread -l util -l dl

If I execute my example with the static version of Python in argument, it works.

If I execute it on the dynamic version of Python in argument, I get the following error (it happens in Py_Initialize()):

> ./hello /home/caduchon/softs/python/2.7.9/64/gcc/4.8.5/dynamic
Fatal Python error: PyThreadState_Get: no current thread
Aborted (core dumped)

I have no idea why it works with static version and it doesn't with the dynamic one. How can I solve this kind of problem ?

EDIT: my script installing Python is the following:

#!/bin/bash

WORKDIR=/home/caduchon/tmp/install_python_2_7_13
ARCHIVEDIR=/home/caduchon/downloads/python
PYTHON_VERSION='2.7.13'
EZ_SETUP_VERSION='0.9'
SETUPTOOLS_VERSION='34.1.0'
CYTHON_VERSION='0.25.2'
NUMPY_VERSION='1.12.0'
SCIPY_VERSION='0.18.1'
MATPLOTLIB_VERSION='2.0.0'
INSTALLDIR=/home/caduchon/softs/python/$PYTHON_VERSION/64/gcc/4.8.5
LAPACKDIR=/home/caduchon/softs/lapack/3.6.1/64/gcc/4.8.5

### Tkinter ###
echo "Install Tkinter"
sudo apt-get install tk-dev

### Workdir ###
echo "Create workdir"
mkdir -p $WORKDIR/static
mkdir -p $WORKDIR/dynamic

### Python
for x in static dynamic
do
    echo "Install Python ($x)"
    cd $WORKDIR/$x
    echo "  extract archive"
    cp $ARCHIVEDIR/Python-$PYTHON_VERSION.tgz .
    tar -xzf ./Python-$PYTHON_VERSION.tgz &> archive.log
    cd ./Python-$PYTHON_VERSION
    echo "  configure"
    if [ "$x" = "static" ]
    then
        ./configure --prefix=$INSTALLDIR/$x --libdir=$INSTALLDIR/$x/lib &> configure.log
    else
        export LD_RUN_PATH=$INSTALLDIR/$x/lib
        ./configure --enable-shared --prefix=$INSTALLDIR/$x --exec-prefix=$INSTALLDIR/$x --libdir=$INSTALLDIR/$x/lib &> configure.log
    fi
    echo "  build"
    make &> make.log
    echo "  install"
    make install &> make_install.log
    echo "  done"
done

### setuptools
for x in static dynamic
do
    echo "Install setuptools ($x)"
    cd $WORKDIR/$x
    echo "  extract archives"
    cp $ARCHIVEDIR/ez_setup-$EZ_SETUP_VERSION.tar.gz .
    tar -xzf ./ez_setup-$EZ_SETUP_VERSION.tar.gz &> archive.log
    cp $ARCHIVEDIR/setuptools-$SETUPTOOLS_VERSION.zip .
    unzip ./setuptools-$SETUPTOOLS_VERSION.zip &> archive.log
    cp ./ez_setup-$EZ_SETUP_VERSION/ez_setup.py ./setuptools-$SETUPTOOLS_VERSION/.
    cd ./setuptools-$SETUPTOOLS_VERSION
    echo "  install"
    $INSTALLDIR/$x/bin/python ./ez_setup.py &> setup.log
    echo "  done"
done

### Cython
for x in static dynamic
do
    echo "Install Cython ($x)"
    cd $WORKDIR/$x
    echo "  extract archive"
    cp $ARCHIVEDIR/Cython-$CYTHON_VERSION.tar.gz .
    tar -xzf ./Cython-$CYTHON_VERSION.tar.gz &> archive.log
    cd ./Cython-$CYTHON_VERSION
    echo "  install"
    $INSTALLDIR/$x/bin/python ./setup.py install &> install.log
    echo "  done"
done

### NumPy
for x in static dynamic
do
    echo "Install NumPy ($x)"
    cd $WORKDIR/$x
    echo "  extract archive"
    cp $ARCHIVEDIR/numpy-$NUMPY_VERSION.zip .
    unzip ./numpy-$NUMPY_VERSION.zip &> archive.log
    cd ./numpy-$NUMPY_VERSION
    echo "  build"
    $INSTALLDIR/$x/bin/python ./setup.py build --fcompiler=gfortran &> build.log
    echo "  install"
    $INSTALLDIR/$x/bin/python ./setup.py install &> install.log
    echo "  done"
done

### SciPy
for x in static dynamic
do
    echo "Install SciPy ($x)"
    cd $WORKDIR/$x
    echo "  extract archive"
    cp $ARCHIVEDIR/scipy-$SCIPY_VERSION.tar.gz .
    tar -xzf ./scipy-$SCIPY_VERSION.tar.gz &> archive.log
    cd ./scipy-$SCIPY_VERSION
    echo "  configure"
    echo "[DEFAULT]" > ./site.cfg
    echo "library_dirs = $LAPACKDIR/lib64" >> ./site.cfg
    echo "search_static_first = true" >> ./site.cfg
    echo "  build"
    $INSTALLDIR/$x/bin/python ./setup.py build --fcompiler=gfortran &> build.log
    echo "  install"
    $INSTALLDIR/$x/bin/python ./setup.py install &> install.log
    echo "  done"
done

### MatPlotLib
for x in static dynamic
do
    echo "Install MatPlotLib ($x)"
    cd $WORKDIR/$x
    echo "  extract archive"
    cp $ARCHIVEDIR/matplotlib-$MATPLOTLIB_VERSION.tar.gz .
    tar -xzf ./matplotlib-$MATPLOTLIB_VERSION.tar.gz &> archive.log
    cd ./matplotlib-$MATPLOTLIB_VERSION
    echo "  build"
    $INSTALLDIR/$x/bin/python ./setup.py build &> build.log
    echo "  install"
    $INSTALLDIR/$x/bin/python ./setup.py install &> install.log
    echo "  done"
done

EDIT: I identified a possible cause of the problem. If I remove the line export LD_RUN_PATH=$INSTALLDIR/$x/lib in the installation of dynamic Python, my embedded code works. I printed sys.path through embedded code and it point to the right installation. BUT... in this way I can't use the installation directly : it loads a wrong version found in the system (when I print sys.path I see it points to /usr/...). Also, I don't want to have to set environment variables to launch Python because I use several versions of Python on the same machine.

EDIT: Keeping my default installation script of Python, I solve the problem by adding -rdynamic in the options when compiling the C++ example. But I don't well understand what is this option, and which kind of disaster it can cause...

Caduchon
  • 4,574
  • 4
  • 26
  • 67
  • try to add the following parameters: -lboost_python -lpython2.7 – Hugo Corrá Aug 14 '17 at 13:21
  • @HugoCorrá : then I have an error requireing dynamic libraries. – Caduchon Aug 15 '17 at 10:32
  • then you should set your LD_LIBRARY_PATH to the directory where these .so are located. – Hugo Corrá Aug 15 '17 at 13:06
  • @HugoCorrá I need a static link. – Caduchon Aug 15 '17 at 17:23
  • I remember that at some point (maybe when investigating [\[SO\]: What files are required for Py_Initialize to run](https://stackoverflow.com/questions/39539089/what-files-are-required-for-py-initialize-to-run)?) I had the exact problem (well, not involving _Boost_), but I can't remember the resolution. _Python_ and _Boost_ folders are the result of _make_ or _make install_? Q: if _Boost_ lib already links to the (static) _Python_ one, is the latter required by your executable at link time? – CristiFati Aug 15 '17 at 20:20
  • I imagine that the answer to my 1st question is `make install`. Also, could you share the _configure_ cmd for both compiled versions (and if any specific env vars were set: e.g. _CFLAGS_?, or any other custom step). Is the problem reproducible by running `PYTHONHOME=/home/caduchon/softs/python/2.7.9/64/gcc/4.8.5/dynamic /home/caduchon/softs/python/2.7.9/64/gcc/4.8.5/static/bin/python -c "print \"abcd\""`? – CristiFati Aug 15 '17 at 22:23
  • @CristiFati I edited my question adding the scripts I use to install Python and Boost. – Caduchon Aug 16 '17 at 08:39
  • @CristiFati your example run without any error, but I don't understand the objective of this test. – Caduchon Aug 16 '17 at 08:42
  • Actually, it's not related to Boost. As expected, I still have the problem if I remove everything about Boost.Python. I edited the question to simplify it. I just keep the tag "boost-python" because it's a problem related to this context. – Caduchon Aug 16 '17 at 08:54
  • I just wanted to see if it's a _Python_ pure problem (as I encountered in the past several strange situations when altering _PYTHONHOME_). But in the meantime I compiled _2.7.13_ (I see that you upgraded the version as well), and replicated your example code in _C_ (without _Boost_), but I wasn't able to reproduce the problem. – CristiFati Aug 16 '17 at 08:59
  • I have the same problem with Python 2.7.13. I suspect it's a problem with my system configuration, but no idea how to find it. – Caduchon Aug 16 '17 at 09:02
  • Note : I also have the same problem on RedHat 6.4 with gcc 4.4.7. – Caduchon Aug 16 '17 at 09:09
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/152029/discussion-between-cristifati-and-caduchon). – CristiFati Aug 16 '17 at 09:58

1 Answers1

3

If I understand correctly, you want to run the statically linked version while setting the Python home to the dynamically linked version. This doesn't work.

Here's what happens: when you run the Py_Initialize() of the statically linked library, it will at some point try to import the _locale module. Because you set the Python home to the dynamically linked version, it will load $INSTALLDIR/dynamic/lib/python2.7/lib-dynload/_locale.so. This library is dynamically linked against $INSTALLDIR/dynamic/lib/libpython2.7.so.1.0. Now you end up with two copies of the interpreter. The first copy is the statically linked one, which is being initialized. The second copy is uninitialized. When the dynamic module importing mechanism tries to initalize the _locale module, it fails because _locale's init function is referring to the second, completely uninitialized interpreter.

What was the reason you tried this? If you tell us which problem you wanted to solve in the first place, we might be able to help you.

EDIT: (I wrote this after the first edit, I didn't try so far what happens with -rdynamic): When you don't set LD_RUN_PATH, $INSTALLDIR/dynamic/lib/python2.7/lib-dynload/_locale.so is dynamically linked against the system's libpython2.7.so. The reason you don't see an error is that importing the _locale module fails with an ImportError (instead of segfaulting), but this ImportError is catched during interpreter initialization (while previously the segfault couldn't be catched). But if you try in the embedded interpreter to import _locale (or any other extension module like for example _struct), you get an error like this:

Traceback (most recent call last):
  File "<string>", line 1, in <module>
ImportError: $INSTALLDIR/dynamic/lib/python2.7/lib-dynload/_locale.so: undefined symbol: PyUnicodeUCS2_FromObject

EDIT: When compiling hello.cpp against the static Python version, normally most symbols like _PyThreadState_Current don't end up in the dynamic symbol table. This is why you end up with "two copies of the interpreter" as described above and the segfault. However, when passing -rdynamic, these symbols end up in the dynamic symbol table, so now the module init function from the _locale.so of the "dynamic" build refers to _PyThreadState_Current of the "static" build. I'm still not convinced that what you try to do (using a program linked against the "static" build with the Python home of the "dynamic" build) is a good idea, though. ;)

Manuel Jacob
  • 1,897
  • 10
  • 21
  • My client uses Python with PySide (Qt) (working only with dynamic Python). But we don't want to provide dynamic libraries with our exacutable, then we need to link statically. Actually, it's possible, it previously worked. But I don't know how. I can't replicate prevous tests. I probably have a problem in my configuration, but I don't find it. – Caduchon Aug 17 '17 at 08:02
  • What exactly do you mean by PySide "working only with dynamic Python"? – Manuel Jacob Aug 17 '17 at 08:45
  • It's not possible to install PySide on a static python version. It needs a dynamic one. – Caduchon Aug 17 '17 at 08:50
  • See last EDIT in my question. – Caduchon Aug 17 '17 at 08:59
  • @Caduchon I just tried to compile PySide with the "static" Python build and indeed it fails, complaining that some code wasn't compiled with `-fPIC`. I'll try to find out why. Why can't you use a Python build with `--enable-shared`? Also, I expanded my answer a bit, so hopefully that will make it easier for you to understand what's going on. – Manuel Jacob Aug 17 '17 at 16:08