使用动态版本的 Python 执行嵌入的 Python 代码时出现致命的 Python 错误
Posted
技术标签:
【中文标题】使用动态版本的 Python 执行嵌入的 Python 代码时出现致命的 Python 错误【英文标题】:Fatal Python error when using a dynamic version of Python to execute embedded python code 【发布时间】:2017-08-11 15:04:41 【问题描述】:剧透:部分解决(见最后)。
以下是使用 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)
我不知道为什么它适用于静态版本而不适用于动态版本。我该如何解决这种问题?
编辑:我安装 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
编辑:我确定了问题的可能原因。如果我在安装动态 Python 时删除了 export LD_RUN_PATH=$INSTALLDIR/$x/lib
行,我的嵌入式代码就可以工作了。我通过嵌入式代码打印了sys.path
,它指向正确的安装。 BUT... 这样我不能直接使用安装:它加载了系统中发现的错误版本(当我打印sys.path
时,我看到它指向/usr/... )。另外,我不想设置环境变量来启动 Python,因为我在同一台机器上使用了多个版本的 Python。
编辑: 保留我默认的 Python 安装脚本,我通过在编译 C++ 示例时在选项中添加 -rdynamic 来解决问题。但是我不太明白这个选项是什么,以及它会导致什么样的灾难......
【问题讨论】:
尝试添加以下参数:-lboost_python -lpython2.7 @HugoCorrá :那么我有一个需要动态库的错误。 那么你应该将你的 LD_LIBRARY_PATH 设置为这些 .so 所在的目录。 @HugoCorrá 我需要一个静态链接。 我记得在某个时候(也许在调查 [SO]: What files are required for Py_Initialize to run 时?)我遇到了确切的问题(嗯,不涉及 Boost),但我不记得解析度。 Python 和 Boost 文件夹是 make 还是 make install 的结果?问:如果 Boost 库已经链接到(静态)Python 库,那么您的可执行文件在链接时是否需要后者? 【参考方案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
编辑:在针对静态 Python 版本编译 hello.cpp
时,通常大多数符号(如 _PyThreadState_Current
)不会出现在动态符号表中。这就是为什么您最终会得到如上所述的“解释器的两个副本”和段错误。但是,当传递-rdynamic
时,这些符号最终会出现在动态符号表中,因此现在“动态”构建的_locale.so 中的模块init 函数引用“静态”构建的_PyThreadState_Current
。不过,我仍然不相信您尝试做的事情(使用与“静态”构建链接的程序与“动态”构建的 Python 主页)是一个好主意。 ;)
【讨论】:
我的客户端使用 Python 和 PySide (Qt)(仅适用于动态 Python)。但是我们不想为我们的可执行文件提供动态库,那么我们需要静态链接。实际上,这是可能的,它以前有效。但我不知道怎么做。我无法复制以前的测试。我的配置可能有问题,但我没有找到。 PySide“只使用动态 Python”到底是什么意思? 无法在静态 python 版本上安装 PySide。它需要一个动态的。 查看我的问题中的最后一个编辑。 @Caduchon 我只是尝试使用“静态”Python 构建编译 PySide,但确实失败了,抱怨某些代码没有使用-fPIC
编译。我会试着找出原因。为什么不能使用带有--enable-shared
的 Python 构建?另外,我稍微扩展了我的答案,希望这会让你更容易理解发生了什么。以上是关于使用动态版本的 Python 执行嵌入的 Python 代码时出现致命的 Python 错误的主要内容,如果未能解决你的问题,请参考以下文章