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
对象,将其转换为字典,并使其通过加载机制。