正在进行中
文档的此页面尚未完成,仅包含内容草稿。
技术规格:求解器状态#
注意
本文档是技术规格,可能不是了解求解器工作原理的最佳方式。为此,请参阅 conda install 和 Solvers.
The Solver
API 将传递一组 MatchSpec
对象(从现在起,我们将称它们为 specs
)给底层的 SAT 求解器。如何从前缀状态和上下文选项构建此列表并不是一个简单的过程,而是一个复杂的逻辑。如果我们检查参与构建 specs
的成分,就能更好地理解这一点。我们将这样标记它们
以下这些组在求解器尝试过程中不会改变
requested
: 用户明确要求的MatchSpec
对象。installed
: 已安装的包,表示为PrefixRecord
对象。如果环境不存在,则为空。history
: 过去要求的规范:History
。如果环境不存在,则为空。aggressive_updates
: 包含在积极更新列表中的包。这些包始终包含在任何请求中,以确保它们在任何情况下都保持最新。pinned
: 固定到特定版本的包,可以通过pinned_packages
在您的.condarc
中或在$PREFIX/conda-meta/pinned
文件中定义。virtual
: 作为虚拟包公开的系统属性(例如__glibc=2.17
)。它们实际上无法安装或卸载,但它们通过添加运行时约束参与求解器。do_not_remove
: 由于 Conda 打包早期存在较差的元数据,求解器会对这些包进行特殊处理的固定列表。一个遗留问题。
这一个组在求解器生命周期中会改变
conflicting
: 疑似与求解器冲突的规范。
此外,还有两个一开始并不明显的来源。这些没有被标记为来源,但它们确实参与了 specs
集合
在新的环境中,包含在
contex.create_default_packages
列表中的包。这些MatchSpec
对象会注入到每个conda create
命令中,因此求解器会将它们视为用户明确要求的(requested
)。由命令行修饰符添加的规范。这里的规范并不新鲜(它们已经存在于其他类别中),但只有在添加标志后才会出现在
specs
列表中。例如,update --all
将将所有已安装的包添加到specs
列表中,没有版本约束。如果没有此标志,已安装的包仍将出现在specs
列表中,但具有完整约束(第一次尝试的--freeze-installed
默认值),除非冻结尝试失败。
--update-specs
(或任何其他UpdateModifier
)已传递,覆盖了--freeze-installed
。
看到吗?这很复杂。我们还将使用此词汇表来帮助缩小正在进行的更改类型
spec
对象的类型
specs
: 包名称到其当前对应MatchSpec
实例的映射。spec
:MatchSpec
对象的特定实例。精确或冻结
spec
:spec
其中version
和build
字段都用==
运算符(精确匹配)约束。完全约束或严格的
spec
:一个spec
同时包含version
和build
,但并非必须使用等号运算符。它也可以使用不等号(>
,<
等)和模糊匹配(*something*
)。仅版本
spec
:一个spec
仅包含version
字段。没有build
字段。仅名称、裸露或无约束的
spec
:一个spec
不包含version
或build
字段。仅包含包的名称。目标
spec
:一个包含target
字段的spec
。从求解器逻辑中的注释中提取。target
是对环境中当前存在的包的引用。设置target
指示求解器在不必要的情况下不要干扰该包。如果 spec.name 通过包含在specs_to_add
中被修改,我们不会设置target
,因为我们希望求解器修改/更新该包。简而言之:在使用
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、virtual 等安装)
没有历史记录,我们没有处于
--freeze-installed
模式,并且该规范不是潜在的冲突,并且
包名称在显式池索引中找不到,或者,如果找到了,则已安装匹配项可以在该显式池中找到(以确保它会被找到,而不是仅仅因为这个原因而创建更多冲突)。
如果它属于“积极更新”列表,我们将把该
spec
放宽为仅包含名称的spec
。如果满足以下条件,我们将将其转换为目标
spec
规范位于
history
中。在这种情况下,我们将使用其“历史”规范对应项,并将目标设置为已安装匹配项的版本和构建。没有满足以上任何条件。换句话说,如果以上条件都不适用,我们将尽力匹配已安装的包,但如果失败,我们将坚持使用
specs
中已经存在的任何内容。