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

СПОЙЛЕР: частично решено (см. в конце).

Вот пример кода, использующего встроенный 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: мой сценарий установки Python выглядит следующим образом:

#!/bin/bash

WORKDIR=/home/caduchon/tmp/install_python_2sys.path13
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: Сохраняя сценарий установки Python по умолчанию, я решаю проблему, добавляя -rdynamic в параметры при компиляции примера C++. Но я не очень понимаю, что это за вариант, и какую катастрофу он может вызвать...


person Caduchon    schedule 11.08.2017    source источник
comment
попробуйте добавить следующие параметры: -lboost_python -lpython2.7   -  person Hugo Corrá    schedule 14.08.2017
comment
@HugoCorrá: тогда у меня ошибка, требующая динамических библиотек.   -  person Caduchon    schedule 15.08.2017
comment
тогда вы должны установить LD_LIBRARY_PATH в каталог, где находятся эти файлы .so.   -  person Hugo Corrá    schedule 15.08.2017
comment
@HugoCorrá Мне нужна статическая ссылка.   -  person Caduchon    schedule 15.08.2017
comment
Я помню, что в какой-то момент (возможно, при исследовании [SO]): Какие файлы необходимы для запуска Py_Initialize?) У меня была точная проблема (ну, не связанная с Boost), но я не могу вспомнить ее решение. Папки Python и Boost являются результатом make или make install? В: если библиотека Boost уже ссылается на (статическую) библиотеку Python, требуется ли последняя для вашего исполняемого файла во время компоновки?   -  person CristiFati    schedule 15.08.2017
comment
Я предполагаю, что ответ на мой 1-й вопрос make install. Кроме того, не могли бы вы поделиться командой configure для обеих скомпилированных версий (и если были установлены какие-либо конкретные переменные env: например, CFLAGS? или любой другой пользовательский шаг). Воспроизводится ли проблема при запуске 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\""?   -  person CristiFati    schedule 16.08.2017
comment
@CristiFati Я отредактировал свой вопрос, добавив сценарии, которые использую для установки Python и Boost.   -  person Caduchon    schedule 16.08.2017
comment
@CristiFati ваш пример работает без ошибок, но я не понимаю цели этого теста.   -  person Caduchon    schedule 16.08.2017
comment
На самом деле, это не связано с Boost. Как и ожидалось, у меня все еще есть проблема, если я удалю все о Boost.Python. Я отредактировал вопрос, чтобы упростить его. Я просто сохраняю тег boost-python, потому что это проблема, связанная с этим контекстом.   -  person Caduchon    schedule 16.08.2017
comment
Я просто хотел посмотреть, является ли это чистой проблемой Python (как я столкнулся в прошлом с несколькими странными ситуациями при изменении PYTHONHOME). Но тем временем я скомпилировал 2.7.13 (я вижу, что вы также обновили версию) и воспроизвел код вашего примера на C (без Boost), но я не смог воспроизвести проблему.   -  person CristiFati    schedule 16.08.2017
comment
У меня такая же проблема с Python 2.7.13. Я подозреваю, что это проблема с конфигурацией моей системы, но не знаю, как ее найти.   -  person Caduchon    schedule 16.08.2017
comment
Примечание. У меня такая же проблема на RedHat 6.4 с gcc 4.4.7.   -  person Caduchon    schedule 16.08.2017
comment
Давайте продолжим обсуждение в чате.   -  person CristiFati    schedule 16.08.2017


Ответы (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 ссылается на второй, полностью неинициализированный интерпретатор.

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

EDIT: (я написал это после первого редактирования, я пока не пробовал, что происходит с -rdynamic): когда вы не устанавливаете LD_RUN_PATH, $INSTALLDIR/dynamic/lib/python2.7/lib-dynload/_locale.so динамически связывается с системным libpython2.7.so. Причина, по которой вы не видите ошибку, заключается в том, что импорт модуля _locale завершается с ошибкой ImportError (вместо segfault), но эта 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 эти символы попадают в таблицу динамических символов, поэтому теперь функция инициализации модуля из _locale.so "динамической" сборки ссылается на _PyThreadState_Current "статической" сборки. Я все еще не уверен, что то, что вы пытаетесь сделать (используя программу, связанную со «статической» сборкой с домом Python «динамической» сборки), является хорошей идеей. ;)

person Manuel Jacob    schedule 17.08.2017
comment
Мой клиент использует Python с PySide (Qt) (работает только с динамическим Python). Но мы не хотим предоставлять динамические библиотеки с нашим исполняемым файлом, тогда нам нужно скомпоновать статически. Вообще-то возможно, раньше работало. Но я не знаю как. Я не могу воспроизвести предыдущие тесты. У меня, вероятно, проблема в моей конфигурации, но я не нахожу ее. - person Caduchon; 17.08.2017
comment
Что именно вы подразумеваете под PySide, работающим только с динамическим Python? - person Manuel Jacob; 17.08.2017
comment
Невозможно установить PySide на статическую версию Python. Нужен динамический. - person Caduchon; 17.08.2017
comment
Смотрите последнее EDIT в моем вопросе. - person Caduchon; 17.08.2017
comment
@Caduchon Я только что попытался скомпилировать PySide со статической сборкой Python, и действительно это не удалось, жалуясь, что некоторый код не был скомпилирован с помощью -fPIC. Я постараюсь выяснить, почему. Почему вы не можете использовать сборку Python с --enable-shared? Кроме того, я немного расширил свой ответ, так что, надеюсь, вам будет легче понять, что происходит. - person Manuel Jacob; 17.08.2017