正在进行中
此文档页面尚未完成,仅包含内容草稿。
技术规范:solver 状态#
注意
本文档是技术规范,可能不是了解 solver 工作原理的最佳方式。为此,请参阅conda install 和 Solvers。
Solver
API 将传递 MatchSpec
对象集合(从现在开始,我们将它们称为 specs
)到基础 SAT solver。如何从前缀状态和上下文选项构建此列表不是一个简单的过程,而是一个复杂的逻辑。如果我们检查参与构建 specs
的成分,则可以更好地理解这一点。我们将像这样标记它们
以下组在 solver 尝试期间不会更改
requested
:用户明确要求的MatchSpec
对象。installed
:已安装的包,表示为PrefixRecord
对象。如果环境不存在,则为空。history
:过去要求的规范:History
。如果环境不存在,则为空。aggressive_updates
:包含在激进更新列表中的包。这些包始终包含在任何请求中,以确保它们在所有情况下都保持最新。pinned
:通过.condarc
中的pinned_packages
或在$PREFIX/conda-meta/pinned
文件中定义的,固定到特定版本的包。virtual
:作为虚拟包公开的系统属性(例如__glibc=2.17
)。它们实际上无法安装或卸载,但它们通过添加运行时约束来参与 solver。do_not_remove
:由于 conda 包裹早期元数据不佳,solver 特殊处理的固定包列表。遗留的。
这一组在 solver 生命周期中确实会更改
conflicting
:怀疑与 solver 冲突的规范。
此外,还有两个最初不明显的来源。这些未标记为来源,但它们确实参与了 specs
集合
在新环境中,包含在
contex.create_default_packages
列表中的包。这些MatchSpec
对象被注入到每个conda create
命令中,因此 solver 会将它们视为用户明确要求的 (requested
)。通过命令行修饰符添加的规范。此处的规范不是新的(它们已在其他类别中),但它们可能仅在添加标志时才最终出现在
specs
列表中。例如,update --all
会将所有已安装的包添加到specs
列表中,没有版本约束。如果没有此标志,则已安装的包仍将最终出现在specs
列表中,但带有完整约束 (--freeze-installed
首次尝试的默认值),除非冻结尝试失败。
--update-specs
(或任何其他UpdateModifier
)已传递,覆盖--freeze-installed
。
看到了吗?它变得复杂了。我们还将使用此词汇表来帮助缩小正在进行的更改类型
spec
对象的类型
specs
:包名称到其当前对应的MatchSpec
实例的映射。spec
:MatchSpec
对象的特定实例。精确或冻结
spec
:version
和build
字段都使用==
运算符(完全匹配)约束的spec
。完全约束或严格
spec
:version
和build
都已填充,但不一定使用等号运算符的spec
。它也可以是不等式 (>
、<
等) 和模糊匹配 (*something*
)。仅版本
spec
:仅填充version
字段的spec
。build
未填充。仅名称、裸或无约束
spec
:没有version
或build
字段的spec
。只有包的名称。目标
spec
:填充了target
字段的spec
。从 solver 逻辑中的注释中提取target
是对当前环境中存在的包的引用。设置target
指示 solver 如果不是必要,则不要干扰该包。如果 spec.name 通过包含在specs_to_add
中而被修改,我们不会设置target
,因为我们希望 solver 修改/更新该包。TL;DR:当使用
MatchSpec
对象时,为了最大限度地减少版本更改,请设置
MatchSpec(name=name, target=prec.dist_str())
要冻结包,请单独设置
MatchSpec
的所有组件
如果
spec
对象没有形容词,则应假定它正在未经修改地添加到specs
映射中,因为它来自其来源。
池(PackageRecord
对象的集合)
已安装池:已安装的包,按名称分组。每个组应仅包含一个记录!
显式池:完整索引,但为
requested
中的规范而减少。
以下部分将变得枯燥乏味,直奔主题。它们将说明从给定的一组初始条件中期望的输出。至少我们会尝试。考虑到 specs
列表在尝试中一直保留着!换句话说,specs
列表仅在第一次尝试时才真正为空;如果失败,后续尝试将仅覆盖(更新)现有列表。实际上,这只会影响受约束包的约束程度。名称应相同。
这也将取决于我们是添加 (conda install|create|update
) 还是删除 (conda remove
) 包。两者都有一个共同的初始化部分,但之后逻辑是分开的。
公共初始化#
注意:这发生在
Solver._collect_all_metadata()
中
无论我们使用哪种命令类型(install
、update
、create
或 remove
),都会发生这种情况。
从
history
添加规范,如果有的话。从
do_not_remove
添加规范,但仅当specs
中已没有该名称的规范,并且未安装具有该名称的包。
添加
virtual
包作为无约束specs
。添加所有这些已安装的包,作为无约束
specs
,这些包满足以下任何条件历史记录为空(在这种情况下,将添加所有已安装的包)
包名称是
aggresive_updates
的一部分该包不是由
conda
安装的,而是由 pip 或其他 PyPI 工具安装的。
准备索引
此时,填充的 specs
和 requested
规范合并在一起。此临时集合用于确定如何减少索引。
处理 conda install
的规范#
准备#
为请求的规范生成显式池(通过
Resolve._get_package_pool()
)。检测潜在冲突(通过 (
Resolve.get_conflicting_specs()
)。
优化与已安装记录匹配的 specs
#
检查每个
specs
是否与单个已安装的包或无包匹配!如果有两个或多个匹配项,则意味着环境状况不佳,并且基本上已损坏。如果spec
与一个已安装的包匹配(我们称之为已安装匹配项),我们将修改原始spec
。如果满足以下条件,我们将
spec
转换为精确(冻结)规范已安装的匹配项是不可管理的(由 pip 安装、虚拟等)
没有历史记录,我们不在
--freeze-installed
模式下,并且规范不是潜在冲突,并且
在显式池索引中找不到包名称,或者,如果找到,则可以在该显式池中找到已安装的匹配项(以保证将找到它而不是仅仅因为创建更多冲突)。
如果
spec
是激进更新列表的一部分,我们会将其放宽为仅名称spec
。如果满足以下条件,我们会将其转换为目标
spec
规范在
history
中。在这种情况下,我们采用其历史规范对应项,并将目标设置为已安装的匹配版本和构建。以上条件均未满足。换句话说,如果以上条件均不适用,我们将尽力匹配已安装的包,但是如果失败,我们将坚持
specs
中已有的内容。