python 虚拟环境管理探索
最近在搞数据处理相关的事情,涉及到一些 python 的项目,重拾丢掉很多年的 python 技能。
项目过程中碰到了某个依赖包和当前系统的版本不一致的问题,而系统中的包被另外一个项目依赖无法随便改动,于是就找到了 python 虚拟环境相关的东西,打算在做一个简单的总结。
关于 python virtual environment
由于 python 丰富的三方包(PyPI · The Python Package Index),python 项目不可避免会碰到依赖包版本管理的问题,例如依赖特定的版本,所以无法存在一个适用所有项目的 python 环境,因此我们需要具备以项目为粒度的隔离能力,给项目一个不受干扰的依赖环境就是 python 虚拟环境需要解决的问题。
python 虚拟环境管理
python 虚拟环境并不是什么新技术,主要是利用了操作系统中环境变量以及进程间环境隔离的特性。操作系统的环境变量可以为程序提供信息和做信息交换介质,进程可以共享操作系统中的环境变量,也可以为进程指定环境变量,其中 PATH
是很重要的环境变量,用于为操作系统和程序提供可执行文件的访问路径,例如写一个程序 a.sh,存放在 /home/user-name/bin/
中,在命令行中执行 a.sh
,会得到提示“ 无法找到程序 a.sh
”,为了让系统找到,可以将 /home/user-name/bin/
路径加入到 PATH
环境变量中,当输入 a.sh
时,操作系统就会从 PATH
所提供的路径中逐个查找,这时就可以找到了。
python 虚拟环境就是利用这个特性构建的,在激活虚拟环境之时,激活脚本会将当前命令行程序的 PATH
修改为虚拟环境的,这样执行命令就会在被修改的 PATH
中查找,从而避免了原本 PATH
可以找到的命令,从而实现了 python 环境的隔离。
为了让开发容易区分当前环境是否虚拟环境以及是那个虚拟环境,命令提示符前会加上特殊标记,如 venv
virtualenv/venv
在 python 3.3 版本之前 virtualenv 作为独立的模块工具存在(Virtualenv Documentation),后来作为 builtin 的模块成为 venv,两者的用法基本一致:
cd ${PROJECT_DIRECTORY}
python3.10 -m venv venv
source ./venv/bin/activate
#
# your python project development workload
#
deactivate
大多情况下,virtualenv/venv 已经能够解决不同 python 项目对虚拟环境的需求,但是这里存在一个小问题:由于操作步骤过多,开发人员有的时候会忘记激活虚拟环境,甚至在 global 环境安装了本地项目的依赖包。
针对这个问题有两种解决办法:
- IDE 环境帮助自动激活 venv,以 VSCode 为例配置自动激活 venv:
2. 其他更自动化的虚拟环境工具如 direnv,pyenv
direnv
direnv 是一个 shell 扩展,主要的作用是根据当前目录加载环境变量。
direnv 通过配置给 shell 添加勾子 hook,具体配置方案可以参考:Setup | direnv,给 shell 添加勾子的原理是通过 trap
命令劫持 SIGINT
实现 shell 进程之间的通讯,通过当前目录的环境变量配置新建 shell 进程,核心原理:
_direnv_hook() {
local previous_exit_status=$?;
trap -- '' SIGINT;
eval "$("{{.SelfPath}}" export bash)";
trap - SIGINT;
return $previous_exit_status;
};
if ! [[ "${PROMPT_COMMAND:-}" =~ _direnv_hook ]]; then
PROMPT_COMMAND="_direnv_hook${PROMPT_COMMAND:+;$PROMPT_COMMAND}"
fi
direnv 实现 python 虚拟环境管理的具体方式:
curl -sfL https://direnv.net/install.sh | bash
echo 'eval "$(direnv hook bash)"' >> ~/.bashrc
source ~/.bashrc
cd $PROJECT_DIRECTORY
# for python2
echo 'layout python' > .envrc
# for python3
echo 'layout python3' > .envrc
direnv allow
#
# your python project development workload
#
# leave current directory to deactivate python virtual environment
cd ..
pyenv
pyenv 帮助 python 开发者更加容易的在不同版本之间切换,相比较 venv 它有如下特点:
- 相对于其他两个工具, pyenv 更侧重在 python 解释器版本管理上, 比包管理更大一个层级, 使用 pyenv 我可以方便的下载指定版本的 python 解释器, pypy, anaconda 等, 可以随时自由的在 shell 环境中本地、全局切换 python 解释器
- 开发的时候不需要限定某个版本的虚拟环境, 只需要在部署的时候用 pyenv 指定某个版本就好了
- pyenv 切换解释器版本的时候, pip 和 ipython 以及对应的包环境都是一起切换的, 所以如果你要同时运行 ipython2.x 和 ipython3.x 多个解释器验证一些代码时就很方便
- pyenv 也可以创建好指定的虚拟环境, 但不需要指定具体目录, 自由度更高, 使用也简单
# 查看当前版本
pyenv version
# 查看所有版本
pyenv versions
# 查看所有可安装的版本
pyenv install --list
# 安装指定版本
pyenv install 3.6.5
# 安装新版本后rehash一下
pyenv rehash
# 删除指定版本
pyenv uninstall 3.5.2
# 指定全局版本
pyenv global 3.6.5
# 指定多个全局版本, 3版本优先
pyenv global 3.6.5 2.7.14
# 实际上当你切换版本后, 相应的pip和包仓库都是会自动切换过去的
# pyenv 支持包管理需要安装 pyenv-virtualenv 插件
# 创建一个3.6.5版本的虚拟环境, 命名为v365env, 然后激活虚拟环境
$ pyenv virtualenv 3.6.5 v365env
$ pyenv activate v365env
# 关闭虚拟环境
$ pyenv deactivate v365env
conda
conda 是一个环境和依赖包的管理器,在跨平台、语言支持都具有较强的通用性,背后还有 anaconda 这样的商业公司支持。在文档支持上比较完善,可以直接参考官方文档:Managing environments。
conda create -n venv python=3.9
conda activate venv
#
# python project workload
#
conda deactivate