conda config 和上下文#

context 对象是 conda 代码库中许多部分的核心。它充当设置的集中式存储库。通常情况下,您会导入单例并直接访问其(众多)属性。

from conda.base.context import context

context.quiet
# False

此单例从多个可能的来源级联初始化。从优先级最低到最高

  1. Context 类中硬编码的默认值。这些值通过类属性定义。

  2. 在配置文件 (.condarc) 中定义的值,它们有自己的 优先级.

  3. 由相应的命令行参数设置的值(如果有)。

  4. 由相应的 CONDA_* 环境变量定义的值(如果存在)。

实现这种行为的机制是一个包含多种类型对象的复杂对象。

Context 类的解剖#

conda.base.context.Context 是与应用程序无关的 conda.common.configuration.Configuration 类的 conda 特定子类。此类实现了用于实例化每个定义属性的优先级顺序,以及整体验证逻辑和帮助消息报告。但仅此而已,它仅仅是 ParameterLoader 对象的存储,这些对象反过来在每个属性中实例化相关的 Parameter 子类。大致来说

class MyConfiguration(Configuration):
    string_field = ParameterLoader(PrimitiveParameter("default", str))
    list_of_int_field = ParameterLoader(SequenceParameter([1, 2, 3], int))
    map_of_foat_values_field = ParameterLoader(MapParameter({"key": 1.0}, float))

当实例化 MyConfiguration 时,这些类属性将由已使用来自上述优先级链的值填充的 .raw_data 字典填充。 raw_data 字典包含 RawParameter 对象,这些对象被子类化以处理其来源的具体细节(YAML 文件、环境变量、命令行标志)。每个 ParameterLoader 对象将 RawParameter 对象传递给其相关 Parameter 子类的 .load() 方法,这些方法被设计为返回其相应的 LoadedParameter 对象对应物。

这有点令人困惑,但委托方式如下

  1. Configuration 子类解析可能来源的原始值,并将它们存储为相关的 RawParameter 对象,这些对象可以是

    • EnvRawParameter:用于来自环境变量的值

    • ArgParseRawParameter:用于来自命令行标志的值

    • YamlRawParameter:用于来自配置文件的值

    • DefaultValueRawParameter:用于来自传递给 ParameterLoader 的默认值的值

  2. 每个 Configuration 属性都是一个 ParameterLoader,它通过 __get__ 实现 property 协议。这意味着,在访问属性时(例如 MyConfiguration.string_field), ParameterLoader 可以执行加载逻辑。这意味着在原始数据中查找潜在的类型匹配,将它们加载为 LoadedParameter 对象,并将它们与适当的优先级顺序合并。

合并策略取决于 (Loaded)Parameter 子类型。以下是可用子类型的列表

  • PrimitiveParameter:保存一个类型为 strintfloatcomplexboolNoneType 的单个标量值。

  • SequenceParameter:保存其他 Parameter 对象的可迭代项(list)。

  • MapParameter:保存其他 Parameter 对象的映射(dict)。

  • ObjectParameter:保存一个对象,其属性设置为 Parameter 对象。

Parameter 对象的主要目标是实现如何对原始值进行类型化并将其转换为其 Loaded 对应项。这些实现了验证例程并定义了如何合并相同键的参数。

  • PrimitiveLoadedParameter:优先级最高的 value 将替换现有的 value。

  • SequenceLoadedParameter:扩展,不重复,保持优先级。

  • MapLoadedParameter:级联更新,保留最高优先级。

  • ObjectLoadedParameter:与 Map 相同。

完成所有这些步骤后,LoadedParameter 对象将被类型化:此时将执行类型验证。如果一切顺利,您将获得您的值。如果没有,将引发验证错误。

请注意,结果会被缓存以加快后续访问速度。这意味着,即使您更改了负责特定设置的环境变量的值,也不会反映在 context 对象中,直到您使用 conda.base.context.reset_context() 刷新它。

请勿修改 Context 对象!

ParameterLoader 并未实现 property 协议的 __set__ 方法,因此您可以自由地覆盖在 Configuration 子类中定义的属性。您可能会认为这将在经过验证机制后重新定义 value,但事实并非如此。您将只是用原始 value 完全覆盖它,这可能不是您想要的。

相反,请将 context 对象视为不可变的。如果您需要在运行时更改设置,这可能是一个不好的主意。唯一可以接受这种情况的是在测试期间。

在不同来源中设置值#

设置值可能的来源背后有一些魔法。这些是如何与最终的 Configuration 对象关联的,可能一开始并不明显。对于每个 RawParameter 子类,情况都有所不同。

  • DefaultValueRawParameter:用户永远不会看到这个。它只是包装传递给 ParameterLoader 类的默认值。可以忽略。

  • YamlRawParameter:这个从 YAML 文件获取并将其解析为字典。此文件中的键必须与 Configuration 类中的属性名称完全匹配(或其别名之一)。一旦正确设置,匹配就会自动发生。如何解析值取决于 YAML 加载器,由 conda 在内部设置。

  • EnvRawParameter:来自某些环境变量的值可以进入 Configuration 实例,前提是它们以 <APP_NAME>_<PARAMETER_NAME> 格式,全部大写。应用程序名称由 Configuration 子类定义。参数名称由类中的属性名称定义,转换为大写。例如,context.ignore_pinned 可以用 CONDA_IGNORE_PINNED 设置。变量的值根据类型以不同的方式解析。

    • PrimitiveParameter 很容易。环境变量字符串被解析为预期的类型。布尔值有点不同,因为有几个字符串被识别为布尔值,并且以不区分大小写的方式。

      • True 可以用 trueyesony 设置。

      • False 可以用 falseoffnnononnone""(空字符串)设置。

    • SequenceParameter 可以指定自己的分隔符(例如 ,),因此环境变量字符串被处理成列表。

    • MapParameterObjectParameter 不支持用环境变量设置。

  • ArgParseRawParameter:这些有点不同,因为没有自动机制将给定的命令行标志绑定到 context 对象。这意味着,如果您向 Context 类添加了一个新设置,并且您希望在 CLI 中将其作为命令行标志使用,您必须自己添加它。如果是这种情况,请参考 conda.cli.conda_argparse 并确保您 argparse.Argumentdest 值与 Context 中的属性名称匹配。这样,Configuration.__init__ 可以获取 argparse.Namespace 对象,将其转换为字典,并使其通过加载机制。