pybind11生成so无法import解决方案 2022-10-14
最近在使用python调用pybind11生成的so发现了一个问题,对于一个python环境下生成的so,另一个环境用不了。(刚开始也还没发现是环境的问题)
一开始发现的现象是能用vscode生成能使用,但在pycharm下使用不能;一开始怀疑的是pycharm配置问题导致库不能被import(import了会提示找不到这个库)。
后面发现换到某指定环境下就可以成功运行(和python版本有关),再之后发现只有特定python版本用python setup.py build
后产生的so能用特定python进行调用。
为什么呢?我们仔细观察后发现不同so涉及到python版本的前缀是不一样的(比如3.7生成就是37),在编译过程中我们也能看到setup的编译选项中涉及到了指定的python版本信息;所以我们确定了他就是解释器版本相关的,那为什么版本不同的so不可以被python读取呢?
在import一个so库里的类或函数时,有时发现so文件分明就在那路径下,可是总是报错ModuleNotFoundError: No module named,这种错误的可能原因有:
1.首先要确保so所在的路径已经包含在sys.path里了,如果so所在目录已经是在python默认的系统路径里,例如/usr/lib/python3.6/dist-packages/或者/usr/local/lib/python3.6/dist-packages/之下的任何层级的目录,不用做任何设置,如果是其他路径,可以通过设置PYTHONPATH或者程序里使用sys.path.insert()或sys.path.append()把路径添加到sys.path里来。
2.路径包含正确了,检查so库的命名的前缀和import是否不一致,这种so库的命名是有一定规则的,例如,Linux上一般是<so_name>.cpython-
3.命名正确了,检查后面的后缀cpython-
4.路径存在冲突,so分明在某个已包含的目录下存在,没有其他的错误,可还总是报错ModuleNotFoundError,这种情况也是很坑人的,花费了很久时间想不出原因来,就是没想到可能路径上存在重名的冲突,例如,我第一次使用python代码调用mediapipe时出现报错ModuleNotFoundError: No module named 'mediapipe.python._framework_bindings',其他什么错误原因都没发现,郁闷地熬夜,最后发现虽然/usr/local/lib/python3.6/site-packages/mediapipe/python/_framework_bindings.cpython-36m-x86_64-linux-gnu.so是存在的,但是在运行程序的工作目录下也有个使用过用来build出meaidpipe的wheel包的源码目录,由于当前工作 目录加入了sys.path里最前面,于是python搜索路径时自然是优先找的/workspace/mediapipe/python/...,这个下面确实是没有那个so文件,于是把这里的mediapipe目录改名或者移走,问题就消失了。
5.最后,如果是自己实现的so库,要想能被python import,so库的内部实现按规范来。
怎么确认你当前使用的python版本支持哪些后缀的库能被import呢,很简单,执行下面的代码:
import importlib.machinery
print(importlib.machinery.all_suffixes())
此时你就能看到上述so熟悉的身影,这就是为什 么有些so能够被import的底层原因。
Reference