Conda环境是否可以继承基础包?

17
我正在寻找一个解决方案,使环境从根目录继承,但是在寻找答案时似乎存在很多混淆。许多 OP 问题认为他们正在继承包,但实际上并不是这样。因此,搜索结果会找到这些问题,但答案提供了相反的解决方案(或者只是解释他们的错误)。
话虽如此,有一个 OP 实际上有类似的目标。Anaconda 环境是否可以共享软件包? 这个 OP 说他们的硬盘空间不足。暗示“共享”应该使用新环境中已安装的相同软件包。答案(未被接受)是使用 --clone
我还发现了这篇文章,新创建的 conda env 是否继承基本 env 的所有软件包? 它说 --clone 不共享软件包。在这篇文章中,OP 认为他们的新环境“共享”软件包,然后得出结论“共享”软件包不存在。非分离 anaconda 环境的用途是什么? 我测试了 --clone 标志和 Conda 文档中的 "构建相同环境" 选项。两个 env 目录的大小完全相同:2G+。
(base) $ conda list --explicit > spec-file.txt
# Produced Size On Disk: 2.14 GB (2,305,961,984 bytes)

(base) conda create --name myclone --clone root
# Produced Size On Disk, clone: 2.14 GB (2,304,331,776 bytes)

唯一的区别是重新下载软件包建立相同的环境,而复制本地文件则需要 更少的时间
我使用Miniconda来部署CLI工具到同事的工作站。基本上,这些工具都使用相同的软件包,偶尔会有例外,当我需要添加一个特定的模块时,我不想在基本安装中添加它。 目标是使用conda create创建扩展基本软件包的环境,类似于virtualenv --system-site-packages,而不是重复安装。

更新2020-02-08

回应@merv和他链接的这篇文章(为什么要安装软件包而不只是将其链接到特定环境中?),该文章指出Conda venvs默认继承基础软件包。我在本周末又有机会遇到了这个问题。以下是基本情况:

下载了Miniconda安装程序。按以下设置进行安装:

  • 为我安装
  • 安装位置:(C:\Users\xtian\Miniconda3_64) 注意:我添加了_64
  • 高级选项
    • 将Anaconda添加到系统PATH变量中,False
    • 将Anaconda注册为系统Python 3.7,True

我更新了pip和setuptools,

conda update pip setuptools

下面,我列出了base中的软件包:

(base) C:\Users\xtian>conda list
# packages in environment at C:\Users\xtian\Miniconda3_64:
#
# Name                    Version                   Build  Channel
asn1crypto                1.3.0                    py37_0
ca-certificates           2020.1.1                      0
certifi                   2019.11.28               py37_0
cffi                      1.13.2           py37h7a1dbc1_0
chardet                   3.0.4                 py37_1003
conda                     4.8.2                    py37_0
conda-package-handling    1.6.0            py37h62dcd97_0
console_shortcut          0.1.1                         3
cryptography              2.8              py37h7a1dbc1_0
idna                      2.8                      py37_0
menuinst                  1.4.16           py37he774522_0
openssl                   1.1.1d               he774522_3
pip                       20.0.2                   py37_1
powershell_shortcut       0.0.1                         2
pycosat                   0.6.3            py37he774522_0
pycparser                 2.19                     py37_0
pyopenssl                 19.1.0                   py37_0
pysocks                   1.7.1                    py37_0
python                    3.7.4                h5263a28_0
pywin32                   227              py37he774522_1
requests                  2.22.0                   py37_1
ruamel_yaml               0.15.87          py37he774522_0
setuptools                45.1.0                   py37_0
six                       1.14.0                   py37_0
sqlite                    3.31.1               he774522_0
tqdm                      4.42.0                     py_0
urllib3                   1.25.8                   py37_0
vc                        14.1                 h0510ff6_4
vs2015_runtime            14.16.27012          hf0eaf9b_1
wheel                     0.34.2                   py37_0
win_inet_pton             1.1.0                    py37_0
wincertstore              0.2                      py37_0
yaml                      0.1.7                hc54c509_2

然后我成功创建了新的虚拟环境:

(base) C:\Users\xtian>conda create -n wsgiserver
Collecting package metadata (current_repodata.json): done
Solving environment: done

## Package Plan ##

  environment location: C:\Users\xtian\Miniconda3_64\envs\wsgiserver

Proceed ([y]/n)? y

Preparing transaction: done
Verifying transaction: done
Executing transaction: done

我在这里激活新的wsgiserver虚拟环境,列出包并最终使用pip进行测试--但是没有pip!我今天使用了64位和32位安装程序进行测试:

(base) C:\Users\xtian>conda activate wsgiserver

(wsgiserver) C:\Users\xtian>conda list
# packages in environment at C:\Users\xtian\Miniconda3_64\envs\wsgiserver:
#
# Name                    Version                   Build  Channel

(wsgiserver) C:\Users\xtian>pip
'pip' is not recognized as an internal or external command,
operable program or batch file.

3
我不想用Python CLI安装超过2G的文件,以免触怒IT神。 (>_<) - xtian
5
所以,你的意思是想要n个环境,这些环境都有相同的2GB软件包集合,并且在不同的环境中安装不同的软件子集。"共享的"软件包应该只安装一次,然后以某种方式链接以供每个环境使用?我也希望能这样做! - Dschoni
"我也想要!",你可以在Linux上使用virtualenv来实现;“肯定有一定程度的链接”,我不知道。在我的系统上,Anaconda3/Lib只有700MB。而另一方面,Anaconda3/pkgs是2.58G——与2.14G相差不大。(顺便说一下,整个目录是4G)。 - xtian
1
没有找到解决这个问题的方法,除了不在Anaconda中进行项目开发。我最多只有两个环境:开发和生产。生产是当前所有脚本的完整生态系统,而开发则是未来的生产环境——也许。 - xtian
1
你看过这个答案吗?你是如何计算磁盘使用量的?如果你单独计算每个文件,那么由于硬链接的存在,你很可能会高估实际的磁盘使用量。 - merv
显示剩余10条评论
1个回答

14

应该让Conda环境继承基础包吗?

不应该。 推荐的工作流程是使用conda create --clone创建一个新的独立环境,然后改变该环境以添加其他软件包。或者,可以将模板环境转储为YAML文件(conda env export > env.yaml),编辑它以包括或删除软件包,然后从中创建一个新环境(conda env create -f env.yaml -n foo)。

对于大多数情况来说,关于这会浪费存储空间的担忧是没有根据的。1 由于Conda使用硬链接来最小化冗余,可能会出现新环境占用更多空间的错觉。有关此问题的更详细分析,请参见问题Why are packages installed rather than just linked to a specific environment?

Conda环境能否继承基础包?

虽然不支持,但是可能是可行的。 首先,明确声明nested activation of Conda environments通过conda activate --stack命令不能启用或帮助允许跨环境继承Python软件包。这是因为它不操作PYTHONPATH,而只是将先前激活的环境保留在PATH上并跳过停用脚本。有关此问题的更详细讨论,请参见this GitHub Issue

现在,我们避免了那个红鲱鱼,让我们谈谈PYTHONPATH。可以使用此环境变量来包括其他要搜索的site-packages目录。因此,天真地说,像这样做

conda activate foo
PYTHONPATH=$CONDA_ROOT/lib/python3.7/site-packages python

应该使用同时包含basefoo软件包的Python启动。这样做的一个关键限制是新环境中的Python必须与base的Python版本匹配,包括次要版本(在本例中为3.7.*)。

详细思考

虽然这将实现软件包继承,但我们需要考虑:这是否真正节省空间?我认为,在实践中,它可能不会,原因如下。
我们不想在物理上复制Python安装,但是新环境必须安装Python以帮助解决我们想要的新软件包。为此,我们不仅应该匹配Python版本(conda create -n foo python=3.7),而且应该与base完全相同的版本。
# first check base's python
conda list -n base '^python$'
# EXAMPLE RESULT
# Name                    Version                   Build  Channel
python                    3.7.6                h359304d_2 

# use this when creating the environment
conda create -n foo python=3.7.6=h359304d_2

这样可以让Conda进行链接并在两个环境中使用相同的物理副本。但是,不能保证Python的依赖项也会重用{{base}}中的软件包。事实上,如果有任何兼容的更新版本可用,它将下载并安装这些版本。
此外,假设我们现在安装{{scikit-learn}}:
conda install -n foo scikit-learn

这将再次检查它及其依赖项的最新版本,而不管这些依赖项的旧但兼容版本是否已通过“base”可用。因此,更多的软件包被不必要地安装到软件包缓存中。
这里的模式似乎是我们真正希望找到一种方法来使foo环境安装新软件包,但使用尽可能多的现有软件包来满足依赖关系。这正是conda create --clone已经实现的。
因此,我失去了继承的动机。

注意

我猜测,对于纯 Python 包的特殊情况,使用pip install --target命令从基础环境安装与基础兼容的包到基础之外的位置可能是可行的。用户可以在启动来自基础python之前将此目录添加到PYTHONPATH中。

这不是我的首选方案。我知道克隆策略是可管理的; 我不知道长期使用此方法会出现什么问题。


[1] 只要软件包缓存(pkgs_dirs)和创建环境的位置(默认为envs_dirs)位于同一卷上,就可以保持这种情况。具有多个卷的配置应该使用符号链接, 这将最终产生相同的效果。除非手动禁用了两种类型的链接,否则Conda将在默默地最小化冗余方面做出不错的工作。
[2] 从技术上讲,人们可能还可以尝试使用--offline标志来强制Conda使用已经缓存的内容。然而,OP的前提是附加包是新的,因此假设我们已经在缓存中拥有兼容版本可能并不明智。

1
太棒了的回答! - CharlesB
2
Re [1],另一个重要的例外是如果环境是由另一个用户创建的。也就是说,如果anaconda是以root身份安装的,并且我使用conda create --clone base --name myenv命令,则会得到base的完整副本,而不是硬链接。看起来可以配置共享软件包缓存,但必须让所有人都有写入权限。 - pavon
1
许多人似乎在共享软件包缓存方面遇到了问题。 - pavon
哎呀,如果我执行 conda create --clone base --name mybase 然后再执行 conda create --clone mybase --name myenv,那么 myenv 也是一个完全独立的副本,没有硬链接。 :( - pavon

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