本地环境运行python程序时报了一个模块导入错误“ImportError: cannot import name TopicPartition”,但是线上环境没有问题。难道本地没安装第三方kafka的包吗?先看下本地和线上安装的kafka的包的情况:
本地安装了3个版本的kafka包
$ ls /usr/lib/python2.7/site-packages/ka
kafka-1.3.5-py2.7.egg/ kafka_python-0.9.5_dev-py2.7.egg kafka_python-1.4.3-py2.7.egg/
线上环境只有1个版本的kafka包
$ ls /usr/lib/python2.7/site-packages/kafka-1.3.5-py2.7.egg/
EGG-INFO kafka
由此可以看出并不是本地没有安装kafka包,而是安装了3个包,导入的时候可能导入了和线上不同的版本的kafka,而且这个版本里没有TopicPartition。如何验证这个想法呢 ?可以通过查看包的版本信息来验证:
>>> import kafka
>>> print kafka.__version__
0.9.5-dev
如上,在python交互模式下导入kafka并查看版本,版本是0.9.5-dev并不是线上的1.3.5版本。而且通过查看0.9.5-dev版本的包可以确定这个版本没有TopicPartition。
那么问题来了,导入的第三方包存在多个版本的时候如何决定导入哪个版本呢 ?怎么做才能成功的导入1.3.5版本而不是0.9.5-dev版本呢 ?
一直没考虑过import是怎么找到包并且导入的,借此机会学一下。python按照如下顺序搜索包(网上查的不一定准确):
1. 程序的主目录
2. PYTHONPATH目录,这是一个环境变量设置,linux下可以通过echo $PYTHONPATH查看,默认是空
3. 标准库目录
4. 任何.pth文件的内容
由于包是通过easy_install工具安装的,查看easy-install.pth发现以下内容:
$ cat /usr/lib/python2.7/site-packages/easy-install.pth
import sys; sys.__plen = len(sys.path)
./xgboost-0.6-py2.7.egg
./redis-2.10.5-py2.7.egg
./protobuf-2.6.1-py2.7.egg
./kafka_python-0.9.5_dev-py2.7.egg
./kazoo-2.2.1dev-py2.7.egg
./kafka-1.3.5-py2.7.egg
发现同时存在0.9.5-dev和1.3.5版本,而且0.9.5-dev在前面,是不是由于顺序的原因导入了0.9.5-dev不得而知,现在把0.9.5-dev版本卸载应该就能成功导入1.3.5版本。可如何卸载easy_install安装的包呢?查到这么一个方式,分两步:
1. easy_install-2.7 -m kafka (这个-m参数其实是多版本的意思,并没有卸载的作用)
2. 手动删除egg包
$ sudo easy_install-2.7 -m kafka
Searching for kafka
Best match: kafka 1.3.5
Processing kafka-1.3.5-py2.7.egg
Removing kafka 1.3.5 from easy-install.pth file
Using /usr/lib/python2.7/site-packages/kafka-1.3.5-py2.7.egg
。。。
执行完之后发现好像把1.3.5版本“卸载”了,再卸载下0.9.5-dev版本:
$ sudo easy_install-2.7 -m kafka_python
Searching for kafka-python
Best match: kafka-python 1.4.3
Processing kafka_python-1.4.3-py2.7.egg
Removing kafka-python 0.9.5-dev from easy-install.pth file
Using /usr/lib/python2.7/site-packages/kafka_python-1.4.3-py2.7.egg
。。。
看起来0.9.5-dev版本也被“卸载”了,现在看下easy-install.pth文件和包的情况,发现pth里没有kafka包的信息了,多个版本的kafka包还在:
$ cat /usr/lib/python2.7/site-packages/easy-install.pth
import sys; sys.__plen = len(sys.path)
./xgboost-0.6-py2.7.egg
./redis-2.10.5-py2.7.egg
./protobuf-2.6.1-py2.7.egg
./kazoo-2.2.1dev-py2.7.egg
$ ls /usr/lib/python2.7/site-packages/kafka
kafka-1.3.5-py2.7.egg/ kafka_python-0.9.5_dev-py2.7.egg kafka_python-1.4.3-py2.7.egg/
接下来试试导入kafka是什么结果,发现导入失败:
Python 2.7.5 (default, Oct 30 2018, 23:45:53)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-36)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import kafka
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: No module named kafka
看来尽管site-packages里有kafka包,但pth文件里没有也会导致导入失败,真是始料未及。
现在把0.9.5-dev版本的包通过rm的方式删除,同时通过easy_install把1.3.5的包安装上应该就可以正常导入了:
删除包文件
$ sudo rm /usr/lib/python2.7/site-packages/kafka_python-0.9.5_dev-py2.7.egg
安装1.3.5版本
$ sudo easy_install-2.7 kafka==1.3.5
Searching for kafka==1.3.5
Best match: kafka 1.3.5
Processing kafka-1.3.5-py2.7.egg
Adding kafka 1.3.5 to easy-install.pth file
查看easy-install.pth文件发现1.3.5版本在里面了
$ cat /usr/lib/python2.7/site-packages/easy-install.pth
import sys; sys.__plen = len(sys.path)
./xgboost-0.6-py2.7.egg
./redis-2.10.5-py2.7.egg
./protobuf-2.6.1-py2.7.egg
./kazoo-2.2.1dev-py2.7.egg
./kafka-1.3.5-py2.7.egg
此时导入kafka成功,版本正确,有TopicPartition:
[GCC 4.8.5 20150623 (Red Hat 4.8.5-36)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import kafka
>>> print kafka.version
<module 'kafka.version' from '/usr/lib/python2.7/site-packages/kafka-1.3.5-py2.7.egg/kafka/version.pyc'>
>>> print kafka.TopicPartition
<class 'kafka.structs.TopicPartition'>
至此程序可以正常跑起来了。python的包导入和包管理真复杂,一个简单的问题折腾了大半天。