virtualenv教程

virtualenv 教程

参考资料:

安装与使用说明

Python–Virtualenv简明教程

安装

通过pip安装virtualenv:

pip install virtualenv

测试你的安装

virtualenv --version

创建环境

查看你机子上不同版本python的地址

which python
which python2
which python2.7
...

会返回一个地址,是你需要的python所在<路径>,复制它。

若想安装的python版本,在本地没有,可以用conda创建一个环境安装这个python:

conda create -n <conda环境名> python=x.x.x

创建虚拟环境

创建方法

可在任意选定<目录>创建虚拟环境,会生成<目录>/<虚拟环境名>/,其中存放python包。

cd <目录>
virtualenv --python=<路径> <虚拟环境名>
pip install virtualenv

--python=<路径> 参数省略,则使用which virtualenv 所返回文件的shebang中的python路径,而非使用which python返回路径。

创建后,<目录>下会生成<虚拟环境的名字>文件夹,其下有bin/,include/,lib/

  • <目录>可为用户根目录(即~/),用于创建整个用户的虚拟环境
  • <目录>可为工程目录,用于创建本工程使用的虚拟环境

权限不足

The program 'virtualenv' is currently not installed. To run 'virtualenv' please ask your administrator to install the package 'python-virtualenv'

请服务器管理员安装python-virtualenv

sudo pip install virtualenv

或者用安装miniconda,然后用conda的pip安装virtualenv:

pip install virtualenv

使用

安装python的包

装pip的软件,必须先到虚拟环境所在目录

cd <目录>

输入一下命令激活python的虚拟环境

cd <目录>
source <虚拟环境名>/bin/activate

激活成功后,用户名前会加(<目录>)

此时,输入以下命令装软件

pip install <软件>

安装完成后,从虚拟环境出来

deactivate

使用python及python的包

当<目录>为某pyhton工程文件夹:

只有在该工程目录,输入source <虚拟环境名>/bin/activate激活后,才能使用该virtualenv虚拟环境,包括python版本、pip、python包,一经激活在任意目录下均可使用。

可移植的环境

若虚拟环境所依赖的外部python删除/受损/移动、项目路径移动、环境文件夹重命名、或把项目/环境移动到另一台操作系统相似的机器,virtualenv所创建的虚拟环境将无法使用。

要解决这些问题,就需要创建可移植的虚拟环境,使得上述操作后,virtualenv虚拟环境还能正常使用,即:能运行python,能调用之前在虚拟环境中安装的python包,能使用pip装卸包。方法如下:

virtualenv --relocatable

命令virtualenv --relocatable <虚拟环境名>,创建可移植的虚拟环境。

  • --relocatable 选项只在 virtualenv 1.x 和 2.x 版本中有效。
  • virtualenv 20.x 版本开始,--relocatable 选项被删除,因此无法在这些版本中使用,需要手动修改虚拟环境内文件,才能创建可移植的虚拟环境。

手动修改虚拟环境内文件

下面不妨取<目录>=venv

内置python

有效方法:手动拷贝conda环境

在用conda的非base环境的python版本,创建virtualenv后,

conda create -n <conda环境名> python=x.x.x
conda activate <conda环境名>
virtualenv venv

只要改好上面这几条,即使conda env remove --name <conda环境名>删除这个conda环境,仍能使用venv/虚拟环境。

  • 拷贝conda环境到env中:

    cp -r /home/haoyu/ENV/localENV/anaconda3/envs/<conda环境名> venv/conda-env

venv/conda-env/中的 conda环境绝对路径/home/haoyu/ENV/localENV/anaconda3/envs/<conda环境名>无需修改,即使删除这个conda环境,也不会影响使用venv/环境。

  • venv/pyvenv.cfg中,改写成相对pyvenv.cfg的路径:

    home = /home/haoyu/ENV/localENV/anaconda3/envs/<conda环境名>/bin
    implementation = CPython
    version_info = 3.11.5.final.0
    virtualenv = 20.28.0
    include-system-site-packages = false
    base-prefix = /home/haoyu/ENV/localENV/anaconda3/envs/<conda环境名>
    base-exec-prefix = /home/haoyu/ENV/localENV/anaconda3/envs/<conda环境名>
    base-executable = /home/haoyu/ENV/localENV/anaconda3/envs/<conda环境名>/bin/python3.10

    改成

    home = ./conda-env/bin
    implementation = CPython
    version_info = 3.10.15.final.0
    virtualenv = 20.28.0
    include-system-site-packages = false
    base-prefix =./conda-env
    base-exec-prefix =./conda-env
    base-executable = ./conda-env/bin/python3.10
  • bin/python -> /home/haoyu/ENV/localENV/anaconda3/envs/<conda环境名>/bin/python,改为指向venv/内的相对路径的链接:

    ln -sf ../conda-env/bin/python python

无效方法1: 拷贝python文件

只拷贝/gpfs-flash/junlab/haoyu/bridge-VC/code/venv/bin/python文件,去替代venv/bin/python链接。

这样无效,执行python获pip命令时,会有如下报错:

Could not find platform independent libraries <prefix>
Could not find platform dependent libraries <exec_prefix>
Consider setting $PYTHONHOME to <prefix>[:<exec_prefix>]
Python path configuration:
  PYTHONHOME = (not set)
  PYTHONPATH = (not set)
  program name = 'python'
  isolated = 0
  environment = 1
  user site = 1
  import site = 1
  sys._base_executable = '/gpfs-flash/junlab/haoyu/bridge-VC/code/venv/bin/python'
  sys.base_prefix = '/home/haoyu/ENV/localENV/anaconda3/envs/bridge-VC'
  sys.base_exec_prefix = '/home/haoyu/ENV/localENV/anaconda3/envs/bridge-VC'
  sys.platlibdir = 'lib'
  sys.executable = '/gpfs-flash/junlab/haoyu/bridge-VC/code/venv/bin/python'
  sys.prefix = '/home/haoyu/ENV/localENV/anaconda3/envs/bridge-VC'
  sys.exec_prefix = '/home/haoyu/ENV/localENV/anaconda3/envs/bridge-VC'
  sys.path = [
    '/home/haoyu/ENV/localENV/anaconda3/envs/bridge-VC/lib/python310.zip',
    '/home/haoyu/ENV/localENV/anaconda3/envs/bridge-VC/lib/python3.10',
    '/home/haoyu/ENV/localENV/anaconda3/envs/bridge-VC/lib/lib-dynload',
  ]
Fatal Python error: init_fs_encoding: failed to get the Python codec of the filesystem encoding
Python runtime state: core initialized
ModuleNotFoundError: No module named 'encodings'

Current thread 0x00007f7da2f9f4c0 (most recent call first):
  <no Python frame>

这是因为,python代码的解释,需要使用conda下的一整个环境,单个python文件无法完成。

无效方法2:–always-copy参数

virtualenv --always-copy venv创建虚拟环境

  • 使用 --always-copy 参数,能把conda环境中的python{,3,3.10}拷贝到 venv/bin/python{,3,3.10}

    若无此参数,则是创建链接:

    venv/bin/python{3,3.10}->venv/bin/python-> /home/haoyu/ENV/localENV/anaconda3/envs/<conda环境名>/bin/python

  • 但是,即便用了 --always-copy 参数,一旦删除这个conda环境,就无法使用venv/虚拟环境:在激活venv/后,执行python pip命令都会报错:

    Could not find platform independent libraries <prefix>
    Could not find platform dependent libraries <exec_prefix>
    ...(同上文)

    其原因和[无效方法1: 拷贝python文件](#无效方法1: 拷贝python文件)相同。

  • 使用 --always-copy 参数,对以下行为没有影响:

    • venv/bin里的shebang是venv/bin/python的绝对路径
    • venv/bin/activate* 里的VIRTUAL_ENV 是 venv/的绝对路径
    • venv/pyvenv.cfg里是conda环境的绝对路径

内置lib

若当前venv环境还依赖于某些lib(比如libsndfile),不能用pip安装,只能用apt/conda/手动 安装,

sudo apt install libsndfile1
# 无需修改LD_LIBRARY_PATH,因为默认LD_LIBRARY_PATH=/opt/cudnn/lib64:/usr/local/cuda/lib6 中已经加载了 libsndfile1
# 或

conda install -c conda-forge libsndfile
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$CONDA_PREFIX/lib

# 或从 https://packages.debian.org/zh-cn/sid/libsndfile1 手动下载,然后修改LD_LIBRARY_PATH以加载之

要想把它内置到venv/里,需要创建venv/lib/libsndfile/,将这些lib文件拷贝到其下。

然后在venv/bin/activate中添加下面一行,使得激活venv环境时,就完成修改LD_LIBRARY_PATH,以调用这些lib文件。

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$VIRTUAL_ENV/lib/libsndfile

如此,即便 apt/conda/手动 卸载了libsndfile,在venv中也还能使用libsndfile。

使用相对路径

若跟进一步,还希望项目<目录> 或 <venv>重命名移动(包括跨机器移动、但必须两台机器的操作系统相似)后,还能使用venv/虚拟环境。则在前述内置python内置lib之外,还需要做如下修改:

  • 修改venv/bin/acitvate*中变量VIRTUAL_ENV(或``virtual_env`)的定义:

    • activate.{csh,fish,nu}:用的是venv/的绝对路径,当前shell不是csh/fish/nu的话,就不用改

    • activate_this.pyactivate.ps1: 原本就用相对当前文件的路径,不需要修改

    • activate:需要改,见下:

      VIRTUAL_ENV=/home/haoyu/project/venv

      改为

      # get absoltae path to the dir this is in, work in bash, zsh
      # if you want transfer symbolic link to true path, just change `pwd` to `pwd -P`
      VIRTUAL_ENV=$(cd "$(dirname "${BASH_SOURCE[0]-$0}")/.."; pwd)
  • 修改shebang:

    venv/bin/下有许多可执行文件(不是activate.*,它们不是可执行文件),例如pip tdqm jupyter,它们是python脚本,但没有.py扩展名,用来调用相应的python包。

    这些文件的第一行是shebang:#!/absolute/path/to/venv/bin/python,表示强制用这个python执行当前可执行文件。

    需要将其换成#!/usr/bin/env python,表示强制用which python执行当可执行前文件。

    批量替换命令,如下:

    find venv/bin/ -type f | xargs -I{} sed -i -E '1s|^#!/.+/python$|#!/usr/bin/env python|' {}
    rg -uu '#!' venv/bin/

    若不这样修改,当venv的路径移动后,调用这些python包时会出现如下bug:

    Could not find platform independent libraries <prefix>
    Could not find platform dependent libraries <exec_prefix>
    ...(同上文)

    注:在venv环境内,每当pip安装新的包后venv/bin/中会自动添加新的python脚本,去调用相应的python包,记得执行上面的批量替换命令,以修改它们的shebang,才能在移动venv后还能使用虚拟环境。

venv命令

python3.3以后的版本中包含venv模块(不需要用户安装其他任何包),其功能和 virtualenv 一样,能在当前目录创建<虚拟环境名>/文件夹,存放python包。

命令如下:

# 创建虚拟环境
python -m venv <虚拟环境名>
# 进入虚拟环境
source <虚拟环境名>/bin/activate
# 退出虚拟环境
deactivate

venv没有类似--relocatable的功能,且生成的<虚拟环境名>/文件夹也和virtualenv生成的几乎一样,里面用到了<虚拟环境名>/绝对路径 和 外部python解释器的绝对路径。

要想用venv创建可移植的虚拟环境,要像virtualenv创建的虚拟环境一样,手动修改虚拟环境内文件