正在进行中

文档的此页面尚未完成,仅包含内容草稿。

技术规格:求解器状态#

注意

本文档是技术规格,可能不是了解求解器工作原理的最佳方式。为此,请参阅 conda installSolvers.

The Solver API 将传递一组 MatchSpec 对象(从现在起,我们将称它们为 specs)给底层的 SAT 求解器。如何从前缀状态和上下文选项构建此列表并不是一个简单的过程,而是一个复杂的逻辑。如果我们检查参与构建 specs成分,就能更好地理解这一点。我们将这样标记它们

以下这些组在求解器尝试过程中不会改变

  1. requested: 用户明确要求的 MatchSpec 对象。

  2. installed: 已安装的包,表示为 PrefixRecord 对象。如果环境不存在,则为空。

  3. history: 过去要求的规范:History。如果环境不存在,则为空。

  4. aggressive_updates: 包含在积极更新列表中的包。这些包始终包含在任何请求中,以确保它们在任何情况下都保持最新。

  5. pinned: 固定到特定版本的包,可以通过 pinned_packages 在您的 .condarc 中或在 $PREFIX/conda-meta/pinned 文件中定义。

  6. virtual: 作为虚拟包公开的系统属性(例如 __glibc=2.17)。它们实际上无法安装或卸载,但它们通过添加运行时约束参与求解器。

  7. do_not_remove: 由于 Conda 打包早期存在较差的元数据,求解器会对这些包进行特殊处理的固定列表。一个遗留问题。

这一个组在求解器生命周期中改变

  1. 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 其中 versionbuild 字段都用 == 运算符(精确匹配)约束。

  • 完全约束或严格的 spec:一个 spec 同时包含 versionbuild,但并非必须使用等号运算符。它也可以使用不等号(>, < 等)和模糊匹配(*something*)。

  • 仅版本 spec:一个 spec 仅包含 version 字段。没有 build 字段。

  • 仅名称、裸露或无约束的 spec:一个 spec 不包含 versionbuild 字段。仅包含包的名称。

  • 目标 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()

无论我们使用哪种类型的命令(installupdatecreateremove),都会发生这种情况。

  1. history 中添加规范,如果有的话。

  2. do_not_remove 中添加规范,但前提是

    • specs 中还没有该名称的规范,并且

    • 环境中没有安装具有该名称的包。

  3. virtual 包添加为无约束的 specs

  4. 将所有已安装的包添加为无约束的 specs,这些包满足以下任何条件

    • 历史记录为空(在这种情况下,将添加所有已安装的包)

    • 包名称是 aggresive_updates 的一部分

    • 包不是由 conda 安装的,而是由 pip 或其他 PyPI 工具安装的。

准备索引

此时,将填充的 specsrequested 规范合并在一起。此临时集合用于确定如何缩减索引。

处理 conda install 的规范#

准备#

  1. 为请求的规范生成显式池(通过 Resolve._get_package_pool())。

  2. 检测潜在的冲突(通过 (Resolve.get_conflicting_specs())。

细化与已安装记录匹配的 specs#

  1. 检查每个 specs 是否与单个已安装的包或根本不匹配!如果存在两个或多个匹配项,则意味着环境状况不佳,实际上已损坏。如果 spec 与一个已安装的包(我们称之为“已安装匹配”)匹配,我们将修改原始 spec

  2. 如果满足以下条件,我们将把 spec 转换为“精确”(冻结)规范

    1. 已安装匹配项无法管理(由 pip、virtual 等安装)

    2. 没有历史记录,我们没有处于 --freeze-installed 模式,并且

      • 该规范不是潜在的冲突,并且

      • 包名称在显式池索引中找不到,或者,如果找到了,则已安装匹配项可以在该显式池中找到(以确保它会被找到,而不是仅仅因为这个原因而创建更多冲突)。

  3. 如果它属于“积极更新”列表,我们将把该 spec 放宽为仅包含名称的 spec

  4. 如果满足以下条件,我们将将其转换为目标 spec

    1. 规范位于 history 中。在这种情况下,我们将使用其“历史”规范对应项,并将目标设置为已安装匹配项的版本和构建。

    2. 没有满足以上任何条件。换句话说,如果以上条件都不适用,我们将尽力匹配已安装的包,但如果失败,我们将坚持使用 specs 中已经存在的任何内容。

处理固定规范#

处理 conda remove 的规范#