Неустранимая ошибка Python при использовании динамической версии Python для выполнения встроенного кода python

SPOILER: частично разрешен (см. в конце).

Вот пример кода с использованием встроенного Python:

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

Я работаю под Linux openSUSE 42.2 с gcc 4.8.5 (но у меня также такая же проблема для openSUSE 13.2 с gcc 4.8.3 или RedHat 6.4 с gcc 4.4.7).

Я собрал статическую и динамическую версию Python 2.7.9 (но у меня тоже такая же проблема с Python 2.7.13).

Я компилирую свой пример, ссылаясь на статическую версию Python с помощью следующей команды:

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

Если я выполнил свой пример со статической версией Python в аргументе, он работает.

Если я выполнил его в динамической версии Python в аргументе, я получаю следующую ошибку (это происходит в 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)

Я понятия не имею, почему он работает со статической версией, а не с динамической. Как я могу решить эту проблему?

EDIT: my script установка Python:

#!/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: Я определил возможную причину проблемы. Если я удаляю строку export LD_RUN_PATH=$INSTALLDIR/$x/lib при установке динамического Python, мой встроенный код работает. Я напечатал sys.path через встроенный код и указал на правильную установку. НО... таким образом я не могу использовать установку напрямую: он загружает неправильную версию, найденную в системе (когда я печатаю sys.path, я вижу, что она указывает на /usr/...). Кроме того, я не хочу устанавливать переменные среды для запуска Python, потому что я использую несколько версий Python на одной машине.

EDIT: Сохраняя мою установку по умолчанию script Python, я решаю проблему, добавляя -rdynamic в параметрах при компиляции примера С++. Но я не совсем понимаю, что это за вариант, и какое бедствие оно может вызвать...

Ответы

Ответ 1

Если я правильно понимаю, вы хотите запустить статически связанную версию, установив дом Python в динамически связанную версию. Это не работает.

Вот что происходит: когда вы запускаете Py_Initialize() статически связанной библиотеки, в какой-то момент попытайтесь импортировать модуль _locale. Поскольку вы устанавливаете дом Python в динамически связанную версию, он загружает $INSTALLDIR/dynamic/lib/python2.7/lib-dynload/_locale.so. Эта библиотека динамически связана с $INSTALLDIR/dynamic/lib/libpython2.7.so.1.0. Теперь вы получаете две копии переводчика. Первая копия - статически связанная, которая инициализируется. Вторая копия неинициализирована. Когда механизм импорта динамического модуля пытается инициализировать модуль _locale, он терпит неудачу, потому что функция _locale init ссылается на второй, полностью неинициализированный интерпретатор.

В чем причина, по которой вы это пробовали? Если вы сообщите нам, какую проблему вы хотели бы решить в первую очередь, мы могли бы вам помочь.

РЕДАКТИРОВАТЬ: (я написал это после первого редактирования, я не пробовал до сих пор, что происходит с -rdynamic): Если вы не установите LD_RUN_PATH, $INSTALLDIR/dynamic/lib/python2.7/lib-dynload/_locale.so динамически связана с системой libpython2.7.so. Причина, по которой вы не видите ошибку, заключается в том, что импорт модуля _locale завершается с помощью ImportError (вместо segfaulting), но этот ImportError улавливается во время инициализации интерпретатора (в то время как ранее segfault не удалось поймать). Но если вы попробуете встроенный интерпретатор импортировать _locale (или любой другой модуль расширения, например, например, _struct), вы получите такую ​​ошибку:

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: При компиляции hello.cpp в отношении статической версии Python обычно большинство символов, таких как _PyThreadState_Current, не входят в таблицу динамических символов. Вот почему вы получаете "две копии интерпретатора", как описано выше, и segfault. Однако при передаче -rdynamic эти символы заканчиваются в таблице динамических символов, поэтому теперь функция init модуля из llocale.so "динамической" сборки ссылается на _PyThreadState_Current "статической" сборки. Я все еще не убежден, что то, что вы пытаетесь сделать (используя программу, связанную с "статической" сборкой с домом Python из "динамической" сборки), является хорошей идеей.;)