python 虚拟环境管理探索
6 min read

python 虚拟环境管理探索

探索一下 python 项目涉及到的虚拟环境管理的问题,同时列举一下主流的虚拟环境管理工具及其用法
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 环境安装了本地项目的依赖包。

针对这个问题有两种解决办法:

  1. IDE 环境帮助自动激活 venv,以 VSCode 为例配置自动激活 venv:
vscode venv auto-activate

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

References

  1. Using Python Environments in Visual Studio Code
  2. Virtualenv
  3. pyenv: Simple Python version management
  4. Managing environments — conda documentation
足迹