在 yaml 中声明 python 模块

Declare python module in yaml

我有一个 yaml 文件,其中有一些字段的值在 python 中是可以理解的,但它们被解析为字符串值,而不是我所指的 python 类型。这是我的样本:

verbose:
   level: logging.DEBUG

很明显我加载的时候是字符串类型

config = yaml.load(args.config.read(), Loader=yaml.SafeLoader)

我不知道如何准确获取 logging.DEBUG 对象,而不是它的字符串。

请注意,我没有寻找配置日志记录来获取记录器的东西。此日志记录只是 python 模块的示例。

编辑:请参阅 AKX 答案。我不知道 logging._nameToLevel 不需要定义您自己的枚举,而且绝对比使用 evel 更好。但是,我决定不删除这个答案,因为我认为使用枚举的当前首选设计(从 python 3.4 开始)值得一提(如果当时可用,它可能会用于日志记录模块)。


如果您绝对确定配置中提供的值是合法的,您可以像这样使用eval

import logging
levelStr = 'logging.DEBUG'
level = eval(levelStr)

但是正如评论中所说,如果您不确定配置文件中存在的值,使用 eval 可能是灾难性的(请参阅评论中 AKX 提供的示例)。

更好的设计是为此定义一个枚举。不幸的是,日志记录模块不提供级别作为枚举(它们只是模块中定义的常量),因此您应该定义自己的级别。

from enum import Enum
class LogLevel(Enum):
    CRITICAL = 50
    FATAL = 50
    ERROR = 40
    WARNING = 30
    WARN = 30
    INFO = 20
    DEBUG = 10
    NOTSET = 0

然后你可以像这样使用它:

levelStr = 'DEBUG'
levelInt = LogLevel[levelStr].value # Comparable with logging.DEBUG which is also an integer

但是要使用它,您必须稍微更改您的 yml 文件并将 logging.DEBUG 替换为 DEBUG

没有开箱即用的方法。最简单和最安全的方法似乎是手动处理这些值,例如:

import logging

class KnownModules:
    logging = logging
    ...


def parse_value(s):
    v = KnownModules
    for p in s.split('.'):
        v = getattr(v, p)  # remember to handle AttributeError
    return v

但是,如果您可以稍微更改 YAML 结构,PyYAML 支持一些 custom YAML tags。例如:

verbose:
    level: !!python/name:logging.DEBUG

将使config['verbose']['level']等于logging.DEBUG(即10)。

考虑到您正在(正确地)使用 SafeLoader,您可能需要通过定义自己的标签来组合这些方法。

YAML 加载程序不知道 logging.DEBUG 可能意味着什么,除了字符串 "logging.DEBUG"(除非它被标记为 YAML 标签)。

对于需要解释为的字符串值,例如对模块属性的引用,你需要事后解析它们,例如

def parse_logging_level(level_string: str):
    module, _, value = level_string.partition(".")
    assert module == "logging"
    return logging._nameToLevel[value]

# ...

yaml_data["verbose"]["level"] = parse_logging_level(yaml_data["verbose"]["level"])