conda 环境可以继承基础包吗?

Posted

技术标签:

【中文标题】conda 环境可以继承基础包吗?【英文标题】:Can conda environment inherit base packages? 【发布时间】:2019-08-08 21:11:13 【问题描述】:

我正在寻找一个环境确实从根继承的解决方案,但寻找答案似乎有很多困惑。许多 OP 问题认为他们正在继承包,而实际上它们不是。所以,搜索结果找到了这些问题,但答案有反解(或者只是解释他们错了)。

也就是说,一个 OP 实际上有类似的目标。 Can packages be shared across Anaconda environments? 这个 OP 说他们的硬盘空间不足。 “共享”的含义应该在新环境中使用相同的安装包。答案(不接受)是使用--clone

我还发现了这个帖子,Do newly created conda envs inherit all packages from the base env?,上面写着--clone 不共享包。在这篇文章中,OP 认为他们的新环境“共享”包,然后得出结论“共享”包不存在。 What is the use of non-separated anaconda environments?

我测试了--clone 标志和"build identical environments" 选项的Conda Docs 指令。两个 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 年 2 月 8 日更新

回复@merv 和他对这篇文章 (Why are packages installed rather than just linked to a specific environment?) 的链接,其中说 Conda venvs 默认继承基本包。这个周末我有另一个机会解决这个问题。这是基本情况:

下载了Miniconda installer。使用设置安装

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

我更新了 pip 和 setuptools,

conda 更新 pip 设置工具

下面,我列出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

那我就成功创建了新的venv:

(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.

【问题讨论】:

那么,你的目标是什么?您想提供本地托管的包缓存,以便更快地进行多次安装(并且在克隆时无需重新下载)? 我不想做的是用 2G 倍数的 Python CLI 安装激怒 IT 之神。 (>_ 因此,您的意思是,您希望拥有 n 个环境,其中包含相同的 2 GB 包集以及不同环境中的不同包子集。 “共享”包应该只安装一次并以某种方式链接以供每个环境使用?我也想要! 没有。除了在其他人描述的 Anaconda 中不进行项目开发外,我还没有找到解决这个问题的方法。我一次不超过两个环境:开发和生产。生产是当前完整的脚本生态系统,而开发是未来的生产——也许吧。 你见过this other answer吗?你是如何计算磁盘使用量的?如果您单独计算每个,那么您可能会因为硬链接而高估实际磁盘使用量。 【参考方案1】:

应该 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

应该启动 Python 并同时提供 basefoo 的包。使其工作的一个关键限制是,新环境中的 Python 必须匹配 base 直到次要版本(在本例中为 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 env 安装新包,但使用尽可能多的现有包来满足依赖关系。这正是conda create --clone 已经在做的事情。2

因此,我完全失去了为继承而烦恼的动力。


注意

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

这不是我的第一选择。我知道克隆策略是可控的;长期来看,我不知道会发生什么。


[1] 只要包缓存的位置 (pkgs_dirs) 和创建环境的位置(默认为 envs_dirs)在相同的音量。具有多个卷的配置should be using softlinks,最终将具有相同的效果。除非手动禁用这两种类型的链接,否则 Conda 将在默默地最小化冗余方面做得不错。

[2] 从技术上讲,人们可能还会尝试使用 --offline 标志来强制 Conda 使用它已经缓存的内容。但是 OP 的前提是附加的包是新的,所以假设我们在缓存中已经有一个兼容的版本可能是不明智的。

【讨论】:

很棒的答案! Re [1],另一个大的例外是如果环境是由另一个用户创建的。也就是说,如果 anaconda 以 root 身份安装,并且我 conda create --clone base --name myenv,我得到一个完整的 base 副本,而不是硬链接。看起来你可以配置一个shared package cache,但它必须是每个人都可以写的。 而且很多人似乎使用共享包缓存have issues。 嗯,如果我这样做 conda create --clone base --name mybase 然后 conda create --clone mybase --name myenv,myenv 也是一个完全独立的副本,没有硬链接。 :(

以上是关于conda 环境可以继承基础包吗?的主要内容,如果未能解决你的问题,请参考以下文章

阻止包安装到conda基础环境

如何防止 Conda 默认激活基础环境?

python开发简介:Conda,Pip虚环境搭建配置与工程基础实践

conda基础命令

Python基础—conda使用笔记

conda:从基础/根环境中删除所有已安装的软件包