最近为了做作业需要安装不同版本的 python,于是考虑使用 pyenv 来管理这些不同的版本。尽管一开始我觉得这个工具用起来非常简单方便,在配置过程中也出现了很多意想不到的问题,花费了我很多时间,所以在这里把整个配置的过程记录下来。
这篇文章分为两个部分,第一部分按顺序介绍 pyenv 正确配置的过程,在第二部分针对配置过程可能遇到的问题给出解决方案。
pyenv 配置过程
pyenv 的安装
只需要按照 官网 上的流程一步步做,就可以顺利安装 pyenv 了。
$ git clone https://github.com/pyenv/pyenv.git ~/.pyenv
$ echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bash_profile
$ echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bash_profile
$ echo -e 'if command -v pyenv 1>/dev/null 2>&1; then\n
eval "$(pyenv init -)"\nfi ' >> ~/.bash_profile
$ exec "$SHELL"
其中,bash_profile
需要根据不同的环境设置,在 bash 中为 .bashrc
,在 zsh 中为 .zshrc
,等等。
具体 python 版本的安装
我做作业是需要用到 3.6.8 版本的 python,所以这边以这个版本为例。由于 pyenv 是以源码编译的方式安装 python,所以需要首先下载好一些编译工具(如 gcc)以及安装必要的依赖。
$ sudo apt-get update
$ sudo apt-get install libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev liblzma-dev
在 官网 上给出了完整的依赖,但有些依赖在 wsl2 的系统上已经预装了,有些依赖会需要下载 python2.7,我就选择了手动确定应该安装哪些包,而没有一股脑全部装了。
安装好之后需要手动设定 C 编译器的选项,以便编译器可以找到我们下载好的头文件以及库文件。
$ CFLAGS=-I/usr/include/openssl
$ LDFLAGS=-L/usr/lib
然后就可以安装 python3.6.8 了。
$ pyenv install 3.6.8
常见问题
pyenv 无法找到 system 版本的 python
下载好后,我就迫不及待将当前版本切换到了 python3.6.8 的版本
$ pyenv global 3.6.8
此时再使用 pyenv versions
查看所有的版本
$ pyenv versions
* 3.6.8 (set by /home/username/.pyenv/version)
我系统原本自带的 python 不见了!进行一番调研才明白,在高版本的 ubuntu 中,python 可执行文件名一般都叫 python2 或者 python3,而 pyenv 是通过 python 这个名字来寻找系统的 python 可执行文件。只需要给 python3 添加一个软链接指向 python 就可以了。
在 Ubuntu20.04 LTS 及更新的版本中,可以直接下载官方插件 python-is-python3(😅
$ sudo apt-get install python-is-python3
而在此前的版本中,则需要手动添加软链接
$ ln -s /usr/bin/python3 /usr/bin/python
此时再执行 pyenv versions
就又可以发现系统自带的 python 了。
$ pyenv versions
system
* 3.6.8 (set by /home/username/.pyenv/version)
ERROR: The Python ssl extension was not compiled. Missing the OpenSSL lib?
在运行 $ pyenv install 3.6.8
后,可能会看到如下错误提示:
$ pyenv install 3.6.8
Downloading Python-3.6.8.tar.xz...
-> https://www.python.org/ftp/python/3.6.8/Python-3.6.8.tar.xz
Installing Python-3.6.8...
WARNING: The Python bz2 extension was not compiled. Missing the bzip2 lib?
WARNING: The Python readline extension was not compiled. Missing the GNU readline lib?
ERROR: The Python ssl extension was not compiled. Missing the OpenSSL lib?
这三个错误的原因是相似的,都是由于当前的机器没有安装对应的 C 语言库,而 pyenv 是从源码编译来安装 python 的。只要按照此前的步骤安装好了相应的依赖就不会有这个问题了。
$ sudo apt-get install libssl-dev libbz2-dev libreadline-dev
$ CFLAGS=-I/usr/include/openssl
$ LDFLAGS=-L/usr/lib
然后再重新安装就好了。
$ pyenv install 3.6.8
zipimport.ZipImportError: can’t decompress data; zlib not available
这个错误与上面也是类似的,安装好缺少的依赖即可。
$ sudo apt-get install zlib1g-dev
UserWarning: Could not import the lzma module.
做作业需要用到 pandas 这个库,通过 pip 我很方便地就完成了安装。然而当我试图在 python 中引入这个库时,却产生了这个问题:
>>> import pandas
/home/username/.pyenv/versions/3.6.8/lib/python3.6/site-packages/pandas/compat/__init__.py:120: UserWarning: Could not import the lzma module. Your installed Python is incomplete. Attempting to use lzma compression will result in a RuntimeError.
warnings.warn(msg)
这里的问题也是缺少此前列举出的依赖,安装好缺少的依赖项,并且重新安装 3.6.8 版本的 python 就可以了。
$ sudo apt-get install liblzma-dev
$ pyenv uninstall 3.6.8
$ pyenv install 3.6.8
重新安装好 pandas 后,再进行测试可以看到已经没有这个问题了。
>>> import pandas
>>> pandas
<module 'pandas' from '/home/username/.pyenv/versions/3.6.8/lib/python3.6/site-packages/pandas/__init__.py'>
No module named _sqlite3
在首次启动 jupyter notebook 时,遇到了这个问题:
$ jupyter notebook
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python3/sqlite3/__init__.py", line 24, in <module>
from dbapi2 import *
File "/usr/local/lib/python3/sqlite3/dbapi2.py", line 27, in <module>
from _sqlite3 import *
ImportError: No module named _sqlite3
这个错误与上面也是类似的,是缺少 sqlite3 这个库。安装好之后再重新安装 python 即可。
$ sudo apt-get install libsqlite3-dev
$ pyenv uninstall 3.6.8
$ pyenv install 3.6.8
安装好 jupyter notebook 再启动可以发现已经可以正常使用了。
jupyter 中 python 内核版本与 pyenv 配置不匹配
pyenv 安装好以后,在命令行界面的一切看起来都非常完美,不同版本的 python 被 pyenv 管理得井井有条,无论是 python 的版本还是当前版本下安装的各种库都与 pyenv 中的设置完美匹配。但是当我在 jupyter notebook 中测试时,就产生了下面的问题。
首先在命令行打开 jupyter notebook:
$ jupyter notebook
在 jupyter notebook 中输入以下语句
In[1]: import sys
sys.version
Out[1]: '3.8.2 (default, Jul 16 2020, 14:00:26) \n[GCC 9.3.0]'
In[2]: !pyenv versions
system
* 3.6.8 (set by /home/xun34/.pyenv/version)
这可真是见了鬼了!pyenv 中明明设置当前的 python 版本为 3.6.8,在 jupyter 中却还是在使用 system python 的内核。除此以外,我在 3.6.8 版本下安装的各种库在 jupyter 环境中也是找不到的。
在网上进行了(好大)一番搜索,才发现原来是因为我在 wsl2 环境中安装了多个不同版本的 jupyter notebook,而它们都被添加到了环境变量里边,可以通过 whereis
命令查看一下
$ whereis jupyter
jupyter: /home/username/.local/bin/jupyter /home/username/.pyenv/shims/jupyter
果真是存在两个 jupyter 版本的,而它们各自是和安装它们的 python 内核关联的。想要在 jupyter notebook 中使用新安装的 python 3.6.8,只需要显式指定该版本的 jupyter notebook 打开即可。
$ /home/username/.pyenv/shims/jupyter notebook
此时就没有上述的问题了。
BONUS
一开始打开 jupyter notebook 时,会很快地闪过一个错误信息,我通过屏幕截图巧妙地捕捉到了这个信息(x),大约长下面这样:

我以为这个错误信息和 jupyter 没有使用 pyenv 配置的 python 版本这个问题有关,因此就花了很大的精力研究这个问题,后来才发现它们其实没有任何关系😢。
产生这个问题是由于 jupyter notebook 试图在 wsl2 中打开浏览器,因此简单地使用
$ jupyter notebook --no-browser
然后再手动在 Windows 端打开浏览器就可以了。或者也可以通过生成 jupyter notebook 的配置文件:
jupyter notebook --generate-config
然后修改该配置文件,使得
c.NotebookApp.use_redirect_file = False
最后将 Windows 端浏览器的路径加入到 .bashrc 文件中
export BROWSER='/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe'
$ source .bashrc
此时就可以自动打开浏览器了!
参考
[1]. pyenv 安装步骤
[2]. pyenv 安装常见问题解答
[3]. pyenv 无法找到 system 版本的 python
[4]. ERROR: The Python ssl extension was not compiled. Missing the OpenSSL lib?
[5]. jupyter not using version set by pyenv
[6]. No module named _sqlite3
[7]. Start : This command cannot be run due to the error: The system cannot find the file specified
[8]. UserWarning: Could not import the lzma module.
强强强