conda config 和 context#
context 对象是 conda 代码库许多部分的核心。它充当设置的集中存储库。您通常导入单例并直接访问其(许多)属性
from conda.base.context import context
context.quiet
# False
此单例从一系列不同的可能来源初始化。从低到高的优先级
硬编码在
Context类中的默认值。这些通过类属性定义。配置文件 (
.condarc) 中定义的值,这些文件有自己的 优先级。由相应的命令行参数设置的值(如果有)。
由其对应的
CONDA_*环境变量定义的值(如果存在)。
实现此行为的机制是一个复杂的对象,涉及多种类型的对象。
Context 类的解剖#
conda.base.context.Context 是特定于 conda 的子类,它是应用程序无关的 conda.common.configuration.Configuration 类。此类实现了每个已定义属性的实例化优先级顺序,以及整体验证逻辑和帮助消息报告。但这只是它,它仅仅是 ParameterLoader 对象的存储,而 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 对象对应项。
这有点令人困惑,但委托是这样发生的
Configuration子类解析可能来源的原始值,并将它们存储为相关的RawParameter对象,这些对象可以是EnvRawParameter:对于那些来自环境变量的对象ArgParseRawParameter:对于那些来自命令行标志的对象YamlRawParameter:对于那些来自配置文件的对象DefaultValueRawParameter:对于那些来自赋予ParameterLoader的默认值的对象
每个
Configuration属性都是一个ParameterLoader,它通过__get__实现property协议。这意味着,在属性访问(例如MyConfiguration.string_field)时,ParameterLoader可以执行加载逻辑。这意味着在原始数据中查找潜在的类型匹配,将它们加载为LoadedParameter对象,并以适当的优先级顺序合并它们。
合并策略取决于 (Loaded)Parameter 子类型。以下是可用子类型的列表
PrimitiveParameter:保存类型为str、int、float、complex、bool或NoneType的单个标量值。SequenceParameter:保存其他Parameter对象的迭代器 (list)。MapParameter:保存其他Parameter对象的映射 (dict)。ObjectParameter:保存一个对象,其属性设置为Parameter对象。
Parameter 对象的主要目标是实现如何将原始值类型化并将其转换为 Loaded 对应项。这些实现了验证例程,并定义了如何合并同一键的参数
PrimitiveLoadedParameter:优先级最高的值替换现有值。SequenceLoadedParameter:扩展且不重复,保持优先级。MapLoadedParameter:级联更新,保留最高优先级。ObjectLoadedParameter:与Map相同。
完成所有这些操作后,LoadedParameter 对象被类型化:这是执行类型验证的时间。如果一切顺利,您就可以很好地获得值。如果不是,则会引发验证错误。
请注意,结果会被缓存,以便更快地进行后续访问。这意味着,即使您更改了负责给定设置的环境变量的值,这也不会反映在 context 对象中,除非您使用 conda.base.context.reset_context() 刷新它。
不要修改 Context 对象!
ParameterLoader 未实现 property 协议的 __set__ 方法,因此您可以自由覆盖在 Configuration 子类中定义的属性。您可能会认为这将在通过验证机制后重新定义值,但这并非如此。您只需使用原始值完全覆盖它,这可能不是您想要的。
相反,请将 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可以使用true、yes、on和y设置。False可以使用false、off、n、no、non、none和""(空字符串)设置。
SequenceParameter可以指定自己的分隔符(例如,),因此环境变量字符串被处理成列表。MapParameter和ObjectParameter不支持使用环境变量设置。
ArgParseRawParameter:这些有点不同,因为没有自动机制将给定的命令行标志与 context 对象联系起来。这意味着,如果您向Context类添加新设置,并且希望在 CLI 中将其作为命令行标志提供,则必须自己添加它。如果是这种情况,请参阅conda.cli.conda_argparse并确保argparse.Argument的dest值与Context中的属性名称匹配。这样,Configuration.__init__可以获取argparse.Namespace对象,将其转换为字典,并使其通过加载机制。