作者: Jim Wang 公众号: 巴博萨船长

摘要:为何需要使用pyenv来管理python的版本?为何需要使用virtualenv来创建python虚拟运行环境?pyenv与pipenv和virtualenv的关系是什么样的?在自己使用pyenv的时候遇见过什么样的问题,又是如何解决所遇问题的。如果遇见诸如“BUILD FAILED”的安装错误,出现该错误的原因可能是什么?

Abstract: Why do we need to manage the use pyenv python version?Why do I need to use virtualenv to create a python virtual runtime environment? What is the relationship with pyenv, pipenv, and virtualenv ?What kind of problems have been encountered when using pyenv and how have they been solved? If you encounter an installation error such as “BUILD FAILED”, what could be the cause of the error?

作者: Jim Wang 公众号: 巴博萨船长

背景

Python的版本管理非常有必要,这种想法在第一次接触到python3的时候就开始有的想法。但是工作中所遇项目主要是在windows系统下,开发环境并不复杂,又使用Eclipse的项目设定结合windows的环境变量。所以也一直没有去尝试着安装配置pyevn,但是自己工作之余学些aws lambda函数时,就遇到了需要隔离python版本与创建虚拟运行环境的使用场景,最终还是逃不过这部分内容,因此也就有了这篇网文。

什么是pyenv和为什么要版本隔离

Pyenv主要是用于Python版本隔离,所谓版本隔离,就是同时安装与使用不同版本的Python在一个主机里,且彼此之间没有交叉也不相互影响。在没有Pyenv之前,在windows系统下也可以实现多python版本共存,可以使用环境变量进行隔离。在Linux系统下则可以使用update-alternatives这一系统命令链接符工具来进行版本管理。

那为什么需要版本管理。假设你有两个Python项目,一个是Python2.x另一个是Python3.x,这是你就可以考虑版本隔离了,或者需要同时测试,管理很多不同版本的Python项目,也可以考虑版本隔离。

什么是virtualenv和为什么需要虚拟环境

virtualenv主要是为了创建Python运行的虚拟环境。所谓虚拟环境,就是Python运行时,库依赖性的隔离。比如项目一需要A依赖包,项目二需要B依赖包。传统习惯是,将A包与B包都使用pip install命令进行安装,就可以同时完成项目一与项目二的开发工作,但是这在开发时是没问题的,但在部署时就不一定了,如果部署环境与开发环境不一致,就可能需要花时间去从新安装依赖包,如果部署平台与开发平台也不一致,或者部署环境中的存储限制就需要在开发的时候创建一个虚拟环境,创建一个最小依赖的开发环境。

常见的一些定义virtualenv和pyvenv(同venv)都是用于创建虚拟运行环境,实现库依赖性的隔离。区别是前者支持Python不同版本,可以为不同python创建虚拟环境,而后者仅支持python3.x,venv仅支持python3.6以后不的版本。另一点区别是,virtualenv是PyPI软件包,pyvenv(同venv)是标准库。

什么是pipenv和什么是依赖管理

另一些常见的定义还有pipevn,pipenv可以视为pip命令和virtualenv命令的组合命令。virtualenv创建的虚拟环境仅仅实现了Python库的依赖性隔离。但是一个虚拟环境下随着项目功能的增加,依赖包的变化,一段时间之后这个虚拟环境下第三方库的依赖具体什么样,就很难弄明白了。pipenv就是为了解决这一问题而存在的。这点很像nodejs的依赖包管理工具npm(或cnpm)。使用Pipenv时,Pipenv会自动管理python库依赖。Pipenv会在创建虚拟环境时自动创建Pipfile和Pipfile.lock文件,之后在安装和卸载第三方依赖包的时候,这两个文件都会得到更新被维护。Pipfile记录项目依赖包列表,Pipfile.lock记录固定版本的详细依赖包列表。

其他相关定义

virtualenvwrapper是对virtualenv命令的扩展。pyenv-virtualenv是pyenv的插件,主要用于确保pyenv和virtualenv能够同时使用。如果使用的是python3.3以上的版本,则默认python自带venv,pyenv-virtualenv插件就是可选内容。pyenv-virtualenvwrapper也是pyenv的插件,主要是为了是pyenv中能集成virtualenvwrapper。

安装pvenv

本文的安装环境为mac os Catalina 版本为10.5.5。在终端输入一下命令, pyenv目前不支持windows (windows可以使用pyenv-win来代替),只支持mac和linux。官方提供了一个安装脚本,安装起来非常简单,它会自动安装pyenvpyenv-virtualenv

1
curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash

也可使用git命令克隆pyenv源代码至本地,命令如下。

1
git clone https://github.com/pyenv/pyenv.git ~/.pyenv

完成上述步骤就需要修改环境变量了,打开bash配置文件.bash_profile或者.bashrc,在文档末尾添加如下内容,

1
2
3
export PATH="$HOME/.pyenv/bin:$PATH"
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"

Mac环境下可以尝试直接使用下列命令来进行安装,需要注意的是,使用brew 安装 pyenv 是不需要进行环境配置的,brew 在安装 pyenv 时已经自动设定了相关内容。

1
brew install pyenv

配置pyenv

完成安装知乎,配置部分的内容其实很简单,仅需要在bash配置文件中添加如下内容,完成pyenv的初始化即可。

1
eval "$(pyenv init -)"

使用pyenv

pyenv提供了11个可用命令,可以参考文档来查看具体内容,在这里简单介绍几个常用命令。

pyenv versions

用于检查当前系统中已经安装的python版本,命令输出如下内容,带星号为当前系统默认使用的python版本。

1
2
3
4
username@mac~ % pyenv versions         
* system (set by /Users/username/.pyenv/version)
2.7.10
2.7.15
pyenv version

用于查看当前系统默认使用的python版本,命令输出如下内容,

1
2
username@mac ~ % pyenv version 
system (set by /Users/username/.pyenv/version)
pyevn install –list

用于检查,所有可安装的python版本,输出内容如下,

1
2
3
4
5
6
7
8
username@mac ~ % pyenv install --list
Available versions:
2.1.3
2.2.3
2.3.7
2.4.0
2.4.1

输出列表里纯数字的表示官方版本,其他的版本注释如下:

  • activepython (ActiveState公司) 主要面向企业用户与数据科学家,可以节省大量精力在Python的组装与管理方面
  • anaconda (Anaconda公司)主要用例包括数学、统计学、工程、数据分析、机器学习以及其他相关应用
  • ironPython 属于一套立足.Net运行时——或者CLR(公共语言运行时)——的Python实现方
  • jython 能够将Python 2.x编译为JVM字节码,并在JVM上运行生成的程序,其仅支持Python 2.x
  • pypy 属于CPython解释器的替代品,其利用即时(JIT)编译以加速Python程序的执行
  • micropython 是Python 3的一个完整软件实现,用C语言编写,被优化于运行在微控制器之上, 如Arduino等
  • miniconda 只包含最基本的内容:python与conda,以及相关的必须依赖项,是conda的最小依赖开发环境
  • stackless 是python的一个增强版本,是python协程的一个实现。 使用它可以避免创建线程所引起的不必要的开销, 同时也可以实现无锁编程。
python install 3.7.0

该命令意指安装Python 3.7.0的官方版本版本,该命令输出如下内容,

1
2
3
4
5
6
7
8
username@mac ~ % pyenv install 3.7.0  
...
python-build: use openssl@1.1 from homebrew
python-build: use readline from homebrew
Installing Python-3.7.0...
python-build: use readline from homebrew
python-build: use zlib from xcode sdk
Installed Python-3.7.0 to /Users/username/.pyenv/versions/3.7.0
python uninstall

该命令用于卸载特定版本的python

python rehash

在安装新一个版本的python后可以执行此命令,使其pyenv更新自己要管理的所有python版本的相关信息。

卸载pyenv

pyenv的卸载非常简单,删除.pyenv目录与bash配置文件的相关内容即可。

更新pyenv

在mac中,可以使用brew upgrade pyenv来完成pyenv的更新,也可以通过从github中克隆新版本的源码到本地安装目录完成相应更新工作。

安装pyenv-virtualenv

在完成python版本的隔离之后,就可以尝试实现项目依赖的隔离,这就需要借助pyenv-virtualenv来完成,安装pyenv-virtualenv也有两种方法,在mac中使用brew 安装或者,从github中克隆源码到本地。

mac中使用brew install pyenv-virtualenv命令则可以完成安装,该命令的输出内容如下,按照输出内容的提示,我们需要在完成安装之后对其进行配置。

1
2
3
4
5
6
7
8
9
10
11
username@mac ~ % brew install pyenv-virtualenv
==> Downloading https://github.com/pyenv/pyenv-virtualenv/archive/v1.1.5.tar.gz
==> Downloading from https://codeload.github.com/pyenv/pyenv-virtualenv/tar.gz/v
######################################################################## 100.0%
==> ./install.sh
==> Caveats
To enable auto-activation add to your profile:
if which pyenv-virtualenv-init > /dev/null; then eval "$(pyenv virtualenv-init -)"; fi
==> Summary
/usr/local/Cellar/pyenv-virtualenv/1.1.5: 22 files, 65.7KB, built in 2 seconds

从github中克隆源码到本地的命令如下,这里需要注意的是,需要在当前用户的home目录运行该命令,因为源码的本地目录为pyenv的子目录,如有必要也需要手动创建子目录plugins。

1
git clone https://github.com/pyenv/pyenv-virtualenv.git .pyenv/plugins/pyenv-virtualenv

配置pyenv-virtualenv

根据brew install pyenv-virtualenv,完成配置仅需在bash配置文件中加入如下内容即可。

1
if which pyenv-virtualenv >/dev/null; then eval "$(pyenv virtualenv-init -)";fi

然后可以使用如下命令,更新bash配置的修改,

1
source .bash_profile

使用pyenv-virtualenv

pyenv-virtualenv的具体使用方法,可以参考文档本文简单介绍一下部分命令。

pyenv virtualenv 版本号 虚拟环境名

比如,由于项目中aws 的lambda函数中使用python 3.6.0 的是最终是需要完成一个python 3.6.0的虚拟运行环境,那么就可以使用如下命令,这里版本号为3.6.0, 虚拟环境名为env360。

1
2
3
username@mac ~ % pyenv virtualenv 3.6.0 env360
Requirement already satisfied: setuptools in /Users/username/.pyenv/versions/3.6.0/envs/env360/lib/python3.6/site-packages
Requirement already satisfied: pip in /Users/username/.pyenv/versions/3.6.0/envs/env360/lib/python3.6/site-packages
pyenv virtualenvs

用于查看当前所有存在的环境名,命令输出内容如下,每个虚拟环境会出现两次,分别为虚拟环境目录的真实目录和目录链接。

1
2
3
4
5
username@mac ~ % pyenv virtualenvs            
3.6.0/envs/env360 (created from /Users/username/.pyenv/versions/3.6.0)
3.8.5/envs/env385 (created from /Users/username/.pyenv/versions/3.8.5)
env360 (created from /Users/username/.pyenv/versions/3.6.0)
env385 (created from /Users/username/.pyenv/versions/3.8.5)
pyenv activate 虚拟环境名

用于激活特定名称的虚拟运行环境,命令输出如下内容。激活后有如下提示信息。如提示信息所述,在非虚拟环境下运行export PYENV_VIRTUALENV_DISABLE_PROMPT=1,则可以在以后激活虚拟环境时,关闭该提示信息。虚拟环境激活之后,命令行提示符部分会有(env360)类似字样,说明当前在env360的虚拟运行环境下。

1
2
3
username@mac ~ % pyenv activate env360
pyenv-virtualenv: prompt changing will be removed from future release. configure `export PYENV_VIRTUALENV_DISABLE_PROMPT=1' to simulate the behavior.
(env360) username@mac ~ %
pyenv virtualenv-delete 虚拟环境名 或 pyenv uninstall 虚拟环境名

这两个命令都是用于删除特定名称的虚拟运行环境,以命令pyenv virtualenv-delete为例,输出入如下内容。命令运行后会有提示信息用于确认操作,输入yes即可确认操作,完成虚拟运行环境的删除工作。

1
2
3
username@mac ~ % pyenv virtualenv-delete env385          
pyenv-virtualenv: remove /Users/username/.pyenv/versions/3.8.5/envs/env385? yes
username@mac ~ %

卸载 pyenv-virtualenv

pyenv-virtualenv的卸载和pyenv的卸载过程类似,删除bash配置文件的相关内容,在pyenv目录中的删除相对应的pyenv-virtualenv子目录即可。

更新 pyenv-virtualenv

如图pyenv一样,在mac中,可以使用brew upgrade pyenv-virtualenv来完成pyenv-virtualenv的更新,也可以通过从github中克隆新版本的源码到本地安装目录完成相应更新工作。

所遇问题与解决方案

问题:Ubuntu 下使用pyenv安装3.7.0版本时,遇到的安装错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
root@R021:~# pyenv install -v 3.6.12
Downloading Python-3.6.12.tar.xz...
-> https://www.python.org/ftp/python/3.6.12/Python-3.6.12.tar.xz
/tmp/python-build.20200916030737.1554 ~
Installing Python-3.6.12...
/tmp/python-build.20200916030737.1554/Python-3.6.12 /tmp/python-build.20200916030737.1554 ~
checking build system type... x86_64-pc-linux-gnu
checking host system type... x86_64-pc-linux-gnu
checking for python3.6... no
checking for python3... python3
checking for --enable-universalsdk... no
checking for --with-universal-archs... no
checking MACHDEP... linux
checking for --without-gcc... no
checking for --with-icc... no
checking for gcc... no
checking for cc... no
checking for cl.exe... cl.exe

BUILD FAILED (Ubuntu 16.04 using python-build 1.2.20-6-gd1ae4a1)

Inspect or clean up the working tree at /tmp/python-build.20200916030737.1554
Results logged to /tmp/python-build.20200916030737.1554.log

出现上述问题的原因为,部分pyenv所需的内容没有安装,可以尝试使用如下命令来检查和安装pyenv的所需组建,然后再使用pyenv安装所需python版本。

1
sudo apt-get install -y make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev xz-utils tk-dev

问题:Mac 下使用pyenv安装3.7.0版本时,遇到的安装错误

1
2
3
4
5
6
7
8
9
10
username@mac ~ % pyenv install 3.7.0 
Downloading openssl-1.0.2k.tar.gz...
-> https://www.openssl.org/source/openssl-1.0.2k.tar.gz
Installing openssl-1.0.2k...

BUILD FAILED (OS X 10.15.5 using python-build 20180424)


configure: error: C compiler cannot create executables
See `config.log' for more details.

上述错误中的关键内容为C 编译器错误,解决方法。这时候就要检查xcode comand line组件是否安装,使用如下命令检测xcode command line是否安装,如果有输出内容,则说明已经安装。

1
2
username@mac ~ % xcode-select -p
/Library/Developer/CommandLineTools

如果想检查xcode command line的版本,则可以使用如下命令:

1
2
3
4
5
6
7
username@mac ~ % pkgutil --pkg-info=com.apple.pkg.CLTools_Executables
package-id: com.apple.pkg.CLTools_Executables
version: 12.0.0.0.1.1599194153
volume: /
location: /
install-time: 1600823154
groups: com.apple.FindSystemFiles.pkg-group

如果没有安装,则需要安装Xcode command line, 然后重新使用pyenv安装所需版本的python。

问题:Mac 终端启动虚拟环境时,遇到错误

在Mac上安装完pyenv和pyenv-virtualenv后,当重新启动终端后,运行pyenv activate xxx启动虚拟环境时会遇到如下提示,

1
2
3
4
Failed to deactivate virtualenv.

Perhaps pyenv-virtualenv has not been loaded into your shell properly.
Please restart current shell and try again.

检查bash的配置.bash_profile中内容也没有发现问题,这时候就要查看Mac中启动的终端时bash还是zsh。bash与zsh都是mac终端自带的shell命令解释器,早期Mac OS系统默认使用bash解释器,在10.15之后的系统中官方推荐使用zsh解释器。如果当前终端启动的是zsh,就需要创建zsh的配置文件.zshrc文件,并在该文件中添加如下相关配置即可,

1
2
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"

当然在Mac中bash和zsh是可以自由切换的,切换方式如下:

  • 切换bash:chsh -s /bin/bash
  • 切换zsh:chsh -s /bin/zsh
  • 终端的系统偏好设置里手动设置。

小结

以上即为本人在安装和配置pyenv和pyenv-virtualenv整个过程,也包括自己在不同系统下安装这两个组建所遇问题和找到的解决方案。Python版本隔离与Python开发运行环境隔离,两者哪一个更有必要了解安装?哪种方案最优?个人觉得没必要过多纠结,适合自己需求的就是最合适的。上述方案也仅为一家之言,并非最优,若读者有别的思路也欢迎关注我的个人微信公众号,一起讨论学习。


版权声明:
文章首发于 Jim Wang's blog , 转载文章请务必以超链接形式标明文章出处,作者信息及本版权声明。