背景
从接触Python以来,一直都是采用virtualenv和virtualenvwrapper来管理不同项目的依赖环境,通过workon、mkvirtualenv等命令进行虚拟环境切换,很是愉快。
然而,最近想让项目能兼容更多的Python版本,例如至少同时兼容Python2.7和Python3.3+,就发现采用之前的方式行不通了。
最大的问题在于,在本地计算机同时安装Python2.7和Python3后,即使分别针对两个Python版本安装了virtualenv和virtualenvwrapper,也无法让两个Python版本的workon、mkvirtualenv命令同时生效。另外一方面,要想在本地计算机安装多个Python版本,会发现安装的成本都比较高,实现方式也不够优雅。
幸运地是,针对该痛点,已经存在一个比较成熟的方案,那就是pyenv。
如下是官方的介绍。
pyenv lets you easily switch between multiple versions of Python. It’s simple, unobtrusive, and follows the UNIX tradition of single-purpose tools that do one thing well.
This project was forked from rbenv and ruby-build, and modified for Python.
本文就针对pyenv最核心的功能进行介绍。
基本原理
如果要讲解pyenv的工作原理,基本上采用一句话就可以概括,那就是:修改系统环境变量PATH。
对于系统环境变量PATH,相信大家都不陌生,里面包含了一串由冒号分隔的路径,例如/usr/local/bin:/usr/bin:/bin。每当在系统中执行一个命令时,例如python或pip,操作系统就会在PATH的所有路径中从左至右依次寻找对应的命令。因为是依次寻找,因此排在左边的路径具有更高的优先级。
而pyenv做的,就是在PATH最前面插入一个$(pyenv root)/shims目录。这样,pyenv就可以通过控制shims目录中的Python版本号,来灵活地切换至我们所需的Python版本。
如果还想了解更多细节,可以查看pyenv的文档介绍及其源码实现。
环境初始化
pyenv的安装方式包括多种,重点推荐采用pyenv-installer的方式,原因主要有两点:
- 通过
pyenv-installer可一键安装pyenv全家桶,后续也可以很方便地实现一键升级; pyenv-installer的安装方式基于GitHub,可保证总是使用到最新版本的pyenv,并且Python版本库也是最新最全的。
install && config
通过如下命令安装pyenv全家桶。
1 | $ curl -L https://raw.githubusercontent.com/pyenv/pyenv-installer/master/bin/pyenv-installer | bash |
内容除了包含pyenv以外,还包含如下插件:
pyenv-doctorpyenv-installerpyenv-updatepyenv-virtualenvpyenv-which-ext
安装完成后,pyenv命令还没有加进系统的环境变量,需要将如下内容加到~/.zshrc中,然后执行source ~/.zshrc。
1 | export PATH=$HOME/.pyenv/bin:$PATH |
完成以上操作后,pyenv就安装完成了。
1 | $ pyenv -v |
如果不确定pyenv的环境是否安装正常,可以通过pyenv doctor命令对环境进行检测。
1 | $ pyenv doctor |
通过检测,可以发现本地环境可能存在的问题,例如,从以上输出可以看出,本地的OpenSSL development header还没有安装。根据提示的问题,逐一进行修复,直到检测不再出现问题为止。
update
通过pyenv update命令,可以更新pyenv全家桶的所有内容。
1 | $ pyenv update |
pyenv的核心使用方法
pyenv的主要功能如下:
1 | $ pyenv -h |
查看所有可安装的Python版本
1 | $ pyenv install --list |
需要注意的是,如果是采用brew命令安装的pyenv,可能会发现Python版本库中没有最新的Python版本。所以建议还是通过GitHub源码方式安装pyenv。
安装指定版本的Python环境
1 | $ pyenv install 3.6.0 |
查看当前系统中所有可用的Python版本
1 | $ pyenv versions |
切换Python版本
pyenv可以从三个维度来管理Python环境,简称为:当前系统、当前目录、当前shell。这三个维度的优先级从左到右依次升高,即当前系统的优先级最低、当前shell的优先级最高。
如果想修改系统全局的Python环境,可以采用pyenv global PYTHON_VERSION命令。该命令执行后会在$(pyenv root)目录(默认为~/.pyenv)中创建一个名为version的文件(如果该文件已存在,则修改该文件的内容),里面记录着系统全局的Python版本号。
1 | $ pyenv global 2.7.13 |
通常情况下,对于特定的项目,我们可能需要切换不同的Python环境,这个时候就可以通过pyenv local PYTHON_VERSION命令来修改当前目录的Python环境。命令执行后,会在当前目录中生成一个.python-version文件(如果该文件已存在,则修改该文件的内容),里面记录着当前目录使用的Python版本号。
1 | $ cat ~/.pyenv/version |
可以看出,当前目录中的.python-version配置优先于系统全局的~/.pyenv/version配置。
另外一种情况,通过执行pyenv shell PYTHON_VERSION命令,可以修改当前shell的Python环境。执行该命令后,会在当前shell session(Terminal窗口)中创建一个名为PYENV_VERSION的环境变量,然后在当前shell的任意目录中都会采用该环境变量设定的Python版本。此时,当前系统和当前目录中设定的Python版本均会被忽略。
1 | $ echo $PYENV_VERSION |
顾名思义,当前shell的Python环境仅在当前shell中生效,重新打开一个新的shell后,该环境也就失效了。如果想在当前shell中取消shell级别的Python环境,采用unset命令重置PYENV_VERSION环境变量即可。
1 | $ cat .python-version |
管理多个依赖库环境
经过以上操作,我们在本地计算机中就可以安装多个版本的Python运行环境,并可以按照实际需求进行灵活地切换。然而,很多时候在同一个Python版本下,我们仍然希望能根据项目进行环境分离,就跟之前我们使用virtualenv一样。
在pyenv中,也包含这么一个插件,pyenv-virtualenv,可以实现同样的功能。
使用方式如下:
1 | $ pyenv virtualenv PYTHON_VERSION PROJECT_NAME |
其中,PYTHON_VERSION是具体的Python版本号,例如,3.6.0,PROJECT_NAME是我们自定义的项目名称。比较好的实践方式是,在PROJECT_NAME也带上Python的版本号,以便于识别。
现假设我们有XDiff这么一个项目,想针对Python 2.7.13和Python 3.6.0分别创建一个虚拟环境,那就可以依次执行如下命令。
1 | $ pyenv virtualenv 3.6.0 py36_XDiff |
创建完成后,通过执行pyenv virtualenvs命令,就可以看到本地所有的项目环境。
1 | $ pyenv virtualenvs |
通过这种方式,在同一个Python版本下我们也可以创建多个虚拟环境,然后在各个虚拟环境中分别维护依赖库环境。
例如,py36_XDiff虚拟环境位于/Users/Leo/.pyenv/versions/3.6.0/envs目录下,而其依赖库位于/Users/Leo/.pyenv/versions/3.6.0/lib/python3.6/site-packages中。
1 | $ pip -V |
后续在项目开发过程中,我们就可以通过pyenv local XXX或pyenv activate PROJECT_NAME命令来切换项目的Python环境。
1 | ➜ MyProjects pyenv local py27_XDiff |
可以看出,切换环境后,pip命令对应的目录也随之改变,即始终对应着当前的Python虚拟环境。
对应的,采用pyenv deactivate命令退出当前项目的Python虚拟环境。
如果想移除某个项目环境,可以通过如下命令实现。
1 | $ pyenv uninstall PROJECT_NAME |
以上便是日常开发工作中常用的pyenv命令,基本可以满足绝大多数依赖库环境管理方面的需求。