问题现象
expat是一个xml文件解析库,python中的xml解析操作往往依赖于其完成。最近使用Python脚本解析一个xml模型文件时,在多处环境(均为Ubuntu 16.04 LTS-amd64)上多次遭遇Python报错提示“ImportError: No module named expat...”,在此进行一个总结。
问题原因
直接原因
直接原因均是由于python未能成功加载系统中libexpat.so导致的,因此问题分析也需要从此处入手。
根本原因
有如下几种情景均能产生上述的直接原因。
- libexpat相关组件未安装:可通过包管理器(rpm或者dpkg)查询expat、libexpat与libexpat-dev是否已经安装
- libexpat.so不存在:若libexpat先前是通过编译安装的,可在/usr/lib或者/lib目录下使用find命令查找libexpat.so是否存在。libexpat.so实际上是一个指向动态库实体的二级软链接,若发现其不存在且实体存在(实体动态库名带版本号,且命名至patch version层级,即版本号带两个小数点),例如在/lib目录下存在libexpat.so.1.6.0,但不存在libexpat.so.1或libexpat.so,将导致libexpat库无法通过软链接被正确加载。此时需要使用ln -nfs命令创建相关的软链接。
- 存在多个不兼容版本的libexpat:若存在不兼容版本的libexpat,可能会导致python调用libexpat时产生未定义符号,从而导致加载失败(未定义符号往往为XML_setHashSalt)。此时先使用ldd命令查看/usr/lib/python2.7/lib-dynload/pyexpat.x86_64-linux-gnu.so所依赖的libexpat.so文件。我自身的情况是在/lib(有的环境是/usr路径下的lib目录)和/usr/local路径(读者需要了解这两个路径的区别)分别存在两个版本不同的libexpat,正常情况应使用前者,但实际使用的后者。据推测后者为编译安装的不兼容版本,前者才是通过包管理器正确安装的libexpat,此时删除后者及其相关的软链接与动态库文件即可。正确的libexpat引用方式如下所示。
root@ubuntu:~# ldd /usr/lib/python2.7/lib-dynload/pyexpat.x86_64-linux-gnu.so
linux-vdso.so.1 => (0x00007ffd9c3fe000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007ff6f7ab7000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff6f76ed000)
libexpat.so.1 => /lib/x86_64-linux-gnu/libexpat.so.1 (0x00007ff6f74c4000)
/lib64/ld-linux-x86-64.so.2 (0x00007ff6f7ee5000)