正在进行中

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

技术规范:solver 状态#

注意

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

Solver API 将传递 MatchSpec 对象集合(从现在开始,我们将它们称为 specs)到基础 SAT solver。如何从前缀状态和上下文选项构建此列表不是一个简单的过程,而是一个复杂的逻辑。如果我们检查参与构建 specs成分,则可以更好地理解这一点。我们将像这样标记它们

以下组在 solver 尝试期间不会更改

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

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

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

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

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

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

  7. do_not_remove:由于 conda 包裹早期元数据不佳,solver 特殊处理的固定包列表。遗留的。

这一组在 solver 生命周期中确实会更改

  1. 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 实例的映射。

  • specMatchSpec 对象的特定实例。

  • 精确或冻结 specversionbuild 字段都使用 == 运算符(完全匹配)约束的 spec

  • 完全约束或严格 specversionbuild 都已填充,但不一定使用等号运算符的 spec。它也可以是不等式 (>< 等) 和模糊匹配 (*something*)。

  • 仅版本 spec填充 version 字段的 specbuild 未填充。

  • 仅名称、裸或无约束 spec:没有 versionbuild 字段的 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()

无论我们使用哪种命令类型(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 安装、虚拟等)

    2. 没有历史记录,我们不在 --freeze-installed 模式下,并且

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

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

  3. 如果 spec 是激进更新列表的一部分,我们会将其放宽为仅名称 spec

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

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

    2. 以上条件均未满足。换句话说,如果以上条件均不适用,我们将尽力匹配已安装的包,但是如果失败,我们将坚持 specs 中已有的内容。

处理固定的规范#

处理 conda remove 的规范#