解决两种场景的问题……
一个传统的方案是每个版本的 Python 可执行文件使用不同的名字,如 python、python2.6、python3。
这种方案需手动指定python版本,使用起来很不方便:
python manage.py runserver
python3 manage.py runserver
python2.6 manage.py runserver
这个场景的典型工具为 virtualenv 和 pyvenv(各工具介绍见《一文了解virtualenv、pyvenv、pyenv、pyenv virtualenv》)。但使用时需要手动激活和注销要使用的虚拟环境,比较麻烦。
# 安装各版本的 Python
pyenv install 2.7.17
pyenv install 3.7.6
pyenv install 3.8.2
# 创建各版本对应的虚拟环境
pyenv virtualenv 2.7.17 venv2
pyenv virtualenv 3.8.2 venv3
# 使用 Python 版本或虚拟环境
pyenv global 2.7.17 # 所有项目默认使用 Python 2.7.17
pyenv local 3.8.2 # 本目录及子目录使用 Python 3.8.2
pyenv local venv2 # 本目录及子目录使用基于 Python 2.7.17 的虚拟环境 venv2
pyenv shell venv3 # 当前 shell 临时使用基于 Python 3.8.2 的虚拟环境 venv3
更重要的是,pyenv 使用了垫片的原理,使用某个 Python 版本或虚拟环境完全是自动的,无需手动指定。例如上面例子设置了 pyenv local venv2,那么进入该目录及其子目录,自动使用的就是基于 Python 2.7.17 的虚拟环境 venv2,而离开该目录后又自动切换到其他 Python 版本或虚拟环境了。
有位伟人说过:
Any problem in computer science can be solved by another layer of indirection.
——计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决。
pyenv 就是使用了中间层的原理来实现的的,即 shims/垫片。
安装 pyenv 后,需要在当前 shell 的配置文件(bash 为 ~/.bashrc,zsh 为 ~/.zshrc 等等)中增加相应命令:
# vim ~/.bashrc
export PATH="/data/sammyshen/.pyenv/bin:$PATH"
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"
这几个命令在每次登陆 shell 时,会更改 PATH 环境变量,将 pyenv 的路径加入到 PATH 环境变量前面。例如:
Linux 执行命令时,是依次遍历 PATH 环境变量的每个路径,查找所执行的命令。当在某个目录下找到第一个匹配时即停止遍历,所以 PATH 环境变量中,前面的路径比后面的路径具有更高的优先级。
这是垫片原理的一个简化示例:
可以看到,在修改 PATH 之前,python 命令使用的是 /usr/bin/python。但通过 export PATH=$PWD:$PATH 将当前路径加入到 PATH 环境变量前面后,python 命令使用的就是当前目录下名为 python 的脚本了。
pyenv 在 ~/.pyenv/shims 目录下创建了各种 python 相关命令的垫片(~/.bashrc 中加入的命令调用 pyenv-rehash 生成的,pyenv install 命令也会调用 pyenv-rehash 进行更新):
所以当我们执行 python 相关的命令时,实际执行的是这些垫片。
这些垫片的内容都是相同的:
从脚本内容可以看出,当我们执行某个命令 program "param1" "param2" ……时,实际执行的是 pyenv exec "program" "param1" "param2" ……。
例如执行 python -V,实际执行的是 pyenv exec python -V。
在 pyenv-exec 命令中,首先会调用 pyenv-version-name 确定 python 版本或虚拟环境版本,具体查找规则为:
在 pyenv-exec 命令中,会再调用 pyenv-which 确定可执行文件 program 的路径。如果前面 pyenv-version-name 确定了 python 版本或虚拟环境版本,则使用 <pyenv 安装路径>/versions/<版本号>/bin/<程序名> 或 <pyenv 安装路径>/versions/<版本号>/env/<虚拟环境名>/bin/<程序名>,否则遍历所有版本号的安装路径,按顺序取第一个匹配到的可执行文件。
确定与版本号对应的可执行文件路径 path 之后,执行以下命令:
exec -a program "$path" "param1" "param2" ……
(注:即执行 "$path" "param1" "param2",并使用 program 作为程序名,程序名即 shell 中的 $0,python 中的 sys.argv0)
例如执行 python -V,确定 pyenv 版本为 2.7.17,对应可执行文件为 ~/.pyenv/versions/2.7.17/bin/python,则执行命令为:
exec -a python ~/.pyenv/versions/2.7.17/bin/python -V
以上就是 pyenv 执行命令的基本原理了。
pyenv 管理版本主要使用三个命令:pyenv global、pyenv local、pyenv shell。
此命令检查版本是否存在,存在则往 <pyenv 安装路径>/version 文件中写入设置的版本号。
此命令检查版本是否存在,存在则往 <当前路径>/.python-version 文件中写入设置的版本号。
此命令检查版本是否存在,存在则往 PYENV_VERSION 环境变量中写入设置的版本号。
按以下顺序依次查找版本号:
如果都没有找到,则使用系统安装的 Python 版本。
以上就是 pyenv 版本管理的基本原理了。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。