我能搬迁一个 virtualenv 吗?

151
这个问题并非重复。它不仅涉及重命名虚拟环境,还包括将其实际移动到不同的目录,可能包括不同用户的目录。这与仅仅重命名虚拟环境不同,尤其是对于不熟悉虚拟环境的人来说。如果我创建了一个虚拟环境,并将其移动到另一个文件夹,它还能正常工作吗?请注意保留HTML标签。
$ virtualenv -p /usr/bin/python3 /home/me/Env/my-python-venv
$ source Env/my-python-venv/bin/activate
(my-python-venv) $ 

...那天晚些时候,虚拟环境移动了...

(my-python-venv) $ deactivate
$ mkdir -p /home/me/PeskyPartyPEnvs
$ mv /home/me/Env/my-python-venv /home/me/PeskyPartyPEnvs/

问题:

这会起作用吗?

$ source /home/me/PeskyPartyPEnvs/my-python-venv/bin/activate
(my-python-venv) $ /home/me/PeskyPartyPEnvs/my-python-venv/bin/pip3 install foaas

我是一名有用的助手,可以为您翻译文本。

我的意思不是关于尝试这个方法是否明智的问题(当然,除非这个智慧是幽默的),而是关于它是否可能的问题。我真的想知道在Python 3中是否可能做到这一点,或者我只能吸氧并克隆它。

我能不能像那样mv一个virtualenv而没有悲伤?我确实想避免悲伤。


此外,我发现目标路径可能是相对的(virtualenv ./my-python-venv),这是具有误导性的,因为它暗示它可能是“可移植”的。将此目标限制为绝对路径将强化这样一个观念:virtualenv目录不能/不应该被移动。 - colm.anseo
11个回答

89
是的,在同一平台上移动虚拟环境是可能的。你可以在现有环境中使用--relocatable
--help命令中可以看到:
--relocatable -- 变更一个已存在的virtualenv环境为可移植,同时修复脚本并使所有.pth文件变为相对路径。
然而,这似乎不会更改activate脚本,只会更改pip*easy_install*脚本。在activate脚本中,$VIRTUAL_ENV环境变量被硬编码为原始的/path/to/original/venv$VIRTUAL_ENV变量用于设置您的活动环境的PATH,因此必须根据新位置进行更改,以便调用pythonpip等而不需要绝对路径。
要解决此问题,您可以更改activate脚本中的$VIRTUAL_ENV环境变量(例如使用sed),然后就可以愉快地使用了。
以下是使用示例:
$ cd ~/first
$ virtualenv my-venv
$ grep 'VIRTUAL_ENV=' my-venv/bin/activate
VIRTUAL_ENV="/home/username/first/my-venv"
$ virtualenv --relocatable my-venv
Making script my-venv/bin/easy_install relative
Making script my-venv/bin/easy_install-2.7 relative
Making script my-venv/bin/pip relative
Making script my-venv/bin/pip2 relative
Making script my-venv/bin/pip2.7 relative
### Note that `activate` has not been touched
$ mkdir ~/second
$ mv my-venv ~/second
$ cd ~/second
$ grep 'VIRTUAL_ENV=' my-venv/bin/activate
VIRTUAL_ENV=/home/username/first/my-venv
### (This variable hasn't been changed, it still refers to the old, now non-existent directory!)
$ sed -i -e 's|username/first|username/second|' my-venv/bin/activate
## sed can be used to change the path.
## Note that the `-i` (in place) flag won't work on all machines. 
$ source my-venv/bin/activate 
(my-venv) $ pip install foass
...
(my-venv) $ python 
[...]
> import foass

太好了,现在你可以安装东西并将它们载入新创建的虚拟环境中。


15
“--relocatable”选项目前存在许多问题,并不能保证在所有情况下都能正常工作。有可能该选项会在virtualenv的未来版本中被废弃。(重点在“可能会被废弃”)请参阅用户指南 - BobTuckerman
1
我在Windows上尝试了这个,错误列表中列出了Scripts目录中的所有脚本(*.py、*.bat、*.ps1)(相当于*nix上的bin),并且显示类似于“activate.ps1不能被设置为相对路径(它不是以#!c:..python.exe开头的普通脚本)。基本上,它抱怨文件头中的哈希-邦不是当前虚拟环境的python.exe,而是我从中移动的那个 - 很容易解决。我查看了那个posh脚本,它已经发现了自己的路径 - 不错。其他一些脚本也不依赖于路径(例如deactivate.bat),所以简而言之,这个方法可行。 - Davos
2
@NathanBasanese 信息“activate.ps1无法相对化”可以忽略,因为该脚本已经是相对路径。在Windows中,此信息并不有用,因为脚本不使用#!指令告诉shell应该执行哪个应用程序,就像在Linux中一样。activate.bat没有被修改,但它没有被使用(至少在Windows 10上,调用activate会启动PoSH脚本),所以我不需要编辑任何脚本。问题出在pip.exe上,它确实有一个硬编码的Python路径,并且需要使用十六进制编辑器进行编辑,或者重新安装。 - Davos
1
正如Nelson在对另一个答案的评论中所指出的那样,python3的venv模块不支持此标志。我可以编辑到你的答案中吗? - Nickolay
3
回想起来,我会说不要这样做。你的Python环境应该能够轻松地通过至少使用pip freeze命令生成一个requirements文件并重新安装所有包,使用Docker(在其中安装虚拟环境非常有效),Conda、Pyenv或其他一些工具进行重建。你应该能够将环境创建和销毁当作不可变基础设施,它们不应该是珍贵的。 - Davos
显示剩余6条评论

70

针对Python 3.3+(使用新的venv内置模块)

简短回答(不考虑版本):

  • 没有干净、直接的方法来移动虚拟环境
  • 只需重新创建,很容易!!


长答案:

从Python v3.3开始,virtualenv包已成为名为venv的内置模块。

其他回答中提到的--relocatable选项未包含在venv中,目前我所知道的没有好的、安全的方法来重命名或重新定位Python虚拟环境。

但是,重新创建一个虚拟环境并安装所有当前安装的软件包是相当简单的。请参见此答案,或参见下面的部分。在此过程中,您可以在任何位置以任何名称重新创建新环境。

在上面链接的答案中,他提到了一些第三方软件包,这些软件包可能支持直接重命名或移动。如果您决定寻求一种移动虚拟环境的方法,您可以看看这些软件包是否也适用于venv

注意:在那个答案中,重点是virtualenv,而不是venv。请参见下一节以了解如何翻译。



venv与旧版virtualenv命令语法的区别

使用venv的命令是:

python -m venv

与其只安装为原始包中的命令的virtualenv不同。其中“python”是指您运行Python可执行文件的方式,可能是各种各样的内容,例如:

  1. python
  2. pypy -3.7或类似内容(适用于Python 3.3+的Windows Python Launcher和捆绑在Windows上的Python,或可单独安装于Linux [和MacOS?]的py包)
  3. python3(Linux环境中双重安装Python 2和3的惯例)
  4. 如果出现问题,请使用要运行的Python可执行文件的绝对路径:例如:c:\program files\python37\python.exe

如果不确定正在运行哪个版本,可以始终使用python --version查找。



如何重新创建虚拟环境

创建/重新创建虚拟环境很容易,只要你用它们工作一段时间后就会变得轻松自然。这个过程反映了你在第一阶段将脚本(及其依赖项)分发为包时所做的操作,以及其他人用于安装你的脚本/包进行进一步开发时所做的操作。

首先,获取虚拟环境中存在的内容的更新列表。激活虚拟环境后,获取它使用的Python版本,并将依赖项列表保存到文件中。

  1. 使用激活的虚拟环境和命令python --version查看它正在使用的Python版本。

    • 这是为了明确 - 你可能想出于各种原因更新Python版本 - 至少更新到最新的修补程序版本
    • 例如,如果现有的虚拟环境使用的是Python v3.7.4,但现在v3.7.6已经发布 - 使用v3.7.6代替,这应该只包含非破坏性的安全和错误修复。
  2. 使用命令python -m pip freeze > requirements.txt创建当前软件包依赖项列表,并将其放入requirements.txt文件中。此命令在Linux或Git Bash中有效,但不确定在Windows的Powershell或命令行中是否有效。

现在创建一个新的虚拟环境,然后添加旧环境中的依赖项。

  1. 创建新的 venv。

    • 确保你使用的是要安装到 venv 的正确版本的 python。
    • 如果你想要完全相同的 Python 版本:
      • 在旧的 venv 中,输入 "python --version",然后确保你使用该版本的 python 命令创建新的 venv。
    • 对于命令中的新 venv 文件夹条目:
      • 要么添加一个绝对或相对路径到所需的最终文件夹位置。
      • 使用 python -m venv my_new_venv 在当前工作目录中的新 my_new_venv 文件夹中创建一个新的虚拟环境。
      • venv 文件夹的名称将是 venv 的名称(在激活时显示在提示符中)。
  2. requirements.txt 文件安装依赖项。

    • python -m pip install -r requirements.txt

你可能需要重新安装开发模式下的本地软件包。

注意,如果你需要查看软件包安装的具体位置,请使用:

  • python -m pip list -v
  • “-v” 或 “verbose” 选项将添加一些关于每个安装的软件包的额外信息,包括它安装在哪个路径下。这对于确保你区分虚拟、用户和系统安装的软件包非常有用。

此时,您只需删除旧的 venv 文件夹和所有内容即可。我建议使用图形用户界面进行操作 - 从 Linux 命令行中进行文件删除通常是永久性的,而一个小错误可能会带来坏消息。


6
有没有一种方法可以复制 venv pip 状态,即无需使用 pip 重新下载所有库? - Aydo
6
我希望你能翻译以下内容:我的问题是我想在一台不允许通过 pip(或几乎任何地方)进行网络流量的机器上运行 Python 脚本。我可以将文件放到该机器上,但它无法使用 pip 进行通信。这显然是一个小众案例,但也正因为如此,我需要移动这些东西。 - Richard Rast
3
你可以使用pip wheel . -w wheels为你的所有软件包创建轮子(wheels),然后在新的虚拟环境中使用pip install --no-index --find-links /path/to/wheels/ -r requirements.txt重新安装这些软件包,完成翻译。 - Niko Pasanen
2
你其实不需要重新安装模块。对于我在 python3.9 中,我只需再次运行 $ python3 -m venv new/path/to/virtenv,它就会修复路径。 - schade96
python.exe 暗示了 Windows 操作系统,但这并不是 OP 的情况。 - thanos.a
显示剩余8条评论

19

但遗憾的是:

不,你不能简单地使用mv。有一些解决方法,但重新安装可能更容易。

(my-python-venv)$ /home/me/PeskyPartyPEnvs/pip3 install foaas
zsh: /home/me/PeskyPartyPEnvs/pip3: bad interpreter: /home/me/Env/my-python-venv/bin/python3: no such file or directory
(my-python-venv)$ deactivate
$ 

...在沮丧的情况下频繁按下 enter,之后以下内容奏效了


$
$
$ pip3 search foaas

除非它不是来自my-python-venv,否则就会很悲伤。

想要将你的virtualenv进行mv并使用它,否则不做修改吗?

简短回答:

我会让博洛米尔说出来,这样他可以说清楚:

好吧,你不能这样做。


3
除非你想进行血腥修改并适当地修改它:问题在于 bin 目录中二进制文件中的链接。如果你知道自己来自哪里,可以使用类似 find bin -type f -exec ex -sc "%s,${FROM},${PWD},g|x" {} \; 的命令,假设你的 bin 和 lib 文件夹在当前虚拟环境文件夹中。我使用这种快速而不严谨的方法复制和移动安装了大量 pip 包的 Python3 虚拟环境。 - Paul Whipp
2
@PaulWhipp 与仅使用 --relocatable 相比,使用该命令是否有任何好处?此外,Nathan,很棒的问题,但这是一个糟糕的答案。接受自己的答案总是有点偏见,除非它写得很好并清楚地列举了选项,但是自己确定这一点也是主观的。 - Davos
//,哈哈,是的,我想这很糟糕。 - Nathan Basanese
2
@Davos --relocatable 对我没用,但我经常通过修改二进制文件来移动 Python3 虚拟环境,到目前为止还没有遇到任何问题。 - Paul Whipp
1
我在暗示你应该将“已接受”的答案更改为其他人(即不是你)选择的答案,也许是由大家选择的那个 :D - Davos
3
问题是“我可以简单地使用mv命令移动虚拟环境吗?”,答案是“不行,有一些变通方法,但重新安装可能更容易”。如果这个回答成为最佳答案,那么将会节省我和其他人的时间。 - Nickolay

7

是的,你可以!(在 windows 中)

解决方法很简单,只需要将你的虚拟环境移动到任何地方,然后编辑 scripts\activate.bat 文件:

  1. Move to the virtual environment to the desired directory

  2. Right-click and edit activate.bat located at venv_folder\scripts.

  3. Change VIRTUAL_ENV variable from:

     set VIRTUAL_ENV=C:\old_directory\venv_name
    

    into

     set VIRTUAL_ENV=C:\new_directory\venv_name
    
  4. Save the edited batch file, and thats it!

注意:我的解决方案应该可以适用于并帮助 windows 用户 设置新的虚拟环境,我怀疑这种方法在其他操作系统上不能正常工作,因为 .bat 源自于 MS-DOS


1
old_directory 更改为 old_directory - 这是一个打字错误吗? - AcK
// 我咬紧牙关打出这句话,但 Windows 得分了一分。 - Nathan Basanese
1
为了在Pycharm中也能够良好运行,您应该从文件->设置...->项目->Python 解释器->[向下箭头]->显示全部...中删除当前配置,并添加新的配置。此外,可能需要取消选择并重新选择当前项目配置的新解释器,从[Pycharm 主视图]->[项目配置名称]->编辑配置...->Python 解释器 - armanexplorer

6
--relocatable参数似乎允许您使用virtualenv实现此目的。

// 这只依赖于相对路径吗,还是会进行其他方面的操作? - Nathan Basanese
1
--relocatable 只适用于已存在的虚拟环境。在环境已经存在之后运行 virtualenv --relocatable my-python-venv - hilcharge
1
--help这将修复脚本并使所有.pth文件相对。 不,它不会使平台无关的库。如果您想将其移动到另一个平台,则需要根据本地Python重新安装它。 - hilcharge
9
Python3的venv模块不支持该标志。 - Nelson

5
是的,如果您没有做任何依赖于虚拟环境当前目录的操作,那么这应该是可能的。
但是,如果可以选择,最好的方法是创建新的虚拟环境并开始使用新的虚拟环境。这是最安全的选择,并且最不可能在以后引起问题。
文档确实提到了

每个虚拟环境都有硬编码的路径信息,

例如,如果您运行了setvirtualenvproject,那么在运行workon ...之后它将无法切换到正确的目录,因此在这种情况下,您需要手动修复它。
一般来说,虚拟环境只是一个带有必要的Python解释器文件和所需软件包的目录。

4

利用本主题和其他类似主题的答案,我编写了一个bash脚本。在虚拟环境目录内部找到并执行该脚本,将有助于您移动虚拟环境。

在执行virtualenv --relocatable yourenv之后,每次移动目录都需要更改VIRTUAL_ENV变量。如果不想手动更改,可以使用此脚本。

#!/bin/bash \n 
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
EXISTING=$(grep 'VIRTUAL_ENV=' bin/activate)  
NEWDIR=VIRTUAL_ENV=\"$DIR\"
sed -i -e "s|$EXISTING|$NEWDIR|" bin/activate
source bin/activate

希望对您有所帮助。

3
我们可以通过创建基础环境的硬链接副本,并使用这个名为venv_move的脚本更新路径,来在虚拟环境之间共享大型模块的存储。
cd /opt
cp -al python3.10-ai python3.10-fastai
venv_move python3.10-fastai

第一个参数是venv的路径。它会删除该路径下的任何__pycache__文件夹。

它会检测旧路径,并在确认后将其替换为当前路径。然后,对于激活脚本中的venv名称也会采用类似的方式进行处理。即使移动到同类型的不同计算机上,它似乎也能正常工作。

该脚本依赖于bash和GNU sed,也就是说它只能在Linux上运行,而不一定适用于Mac OS或Windows操作系统。对于非Linux用户,将其重写为Python代码可能更为合理。

#!/bin/bash -eu
venv=${1%/}

find "$venv" -name __pycache__ | xargs rm -rf --

old=`perl -ne '/VIRTUAL_ENV="(.*?)"/ && print "$1\n"' "$venv/bin/activate"`
new=$PWD/$venv

old2="(`basename "$old"`)"
new2="(`basename "$venv"`)"

if [ "$old" = "$new" ]; then
    echo "venv paths are already set correctly to $new"
else
    files=`fgrep -r "$old" "$venv" -l`
    echo "$files"
    echo "Replace $old with $new in the above files?"
    read -p "[yn] ? " YN
    if [ "$YN" = y ]; then
        sed -i "s:$old:$new:g" $files
    fi

    files=`fgrep -r "$old2" "$venv"/bin/activate* -l`
    echo "$files"
    echo "Replace $old2 with $new2 in the above files?"
    read -p "[yn] ? " YN
    if [ "$YN" = y ]; then
        sed -i "s:$old2:$new2:g" $files
    fi
fi

1
// , “用Python重新编写这个程序是有道理的” 哦耶,Watkins先生,我可能会考虑这么做。 - Nathan Basanese
为什么用Python重写它会有意义呢?我看不出有任何好处。这个脚本已经很好了。 - thanos.a
@thanos.a 一些人使用Windows,非GNU的sed或其他工具。 - Sam Watkins

2

简述

virtualenv-clonevirtualenvwrapper 的一部分。

使用命令 virtualenv-clone /path/to/old/venv /path/to/new/venv 可以克隆虚拟环境。

或者

你也可以尝试使用 cpvirtualenv 命令:

cpvirtualenv /path/to/old/venv /path/to/new/venv

但是,cpvirtualenv 要求 /path/to/old/venv 存在于 $WORKON_HOME 中,否则会失败。既然它调用了 virtualenv-clone,那么你可能更好地使用后者来避免出现错误,例如:

mark@Desktop:~/venvs$ cpvirtualenv ./random/ $WORKON_HOME/random
Copying random as /home/mark/.virtualenvs/venvs/random...
Usage: virtualenv-clone [options] /path/to/existing/venv /path/to/cloned/venv

virtualenv-clone: error: src dir '/home/mark/.virtualenvs/venvs/random' does not exist

根据 virtualenvwrapper 文档 的警告

复制虚拟环境的支持并不完善。每个虚拟环境都将路径信息硬编码到其中,而在某些情况下,复制代码可能不知道需要更新特定文件。请谨慎使用。

它实际上是做什么的? 根据virtualenv-clone PyPi页面

用于克隆非可重定位虚拟环境的脚本。

Virtualenv提供了一种使虚拟环境可重定位的方法,然后我们可以按照自己的意愿进行复制。但是,通过这种方式使虚拟环境可重定位会破坏虚拟环境的无系统包隔离以及其他伴随相对路径和/usr/bin/env的shebang等方面的内容,这可能是不希望的。

此外,.pth和.egg-link重写似乎无法按预期工作。这试图克服这些问题,并提供一种轻松克隆现有虚拟环境的方法。

它执行以下操作:

将sys.argv[1]目录复制到sys.argv[2]

将激活脚本中硬编码的VIRTUAL_ENV变量更新为新的存储库位置。(--relocatable不会触及此内容)

如果指向旧Python,则将bin中各个脚本的shebang更新为新的Python。(版本编号保留。)

它还可以将/usr/bin/env python shebang更改为绝对路径,但此功能目前未公开。

检查克隆虚拟环境的sys.path,如果其中任何路径来自旧环境,则在新环境中查找sys.path中的任何.pth或.egg link文件,并确保将指向旧环境的任何绝对路径更新为新环境。

最后,再次仔细检查sys.path,如果仍然存在旧环境的路径,则会失败。

注意:此脚本需要Python 2.7或3.4+。


1
如前所述,有多种方法可以做到这一点。到目前为止,最好的工具应该是venv-pack。只需一个命令,它就会辛苦地删除硬编码的VIRTUAL_ENV变量,因此一旦打包完成,venv可以放在任何地方并且仍然正常工作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接