在 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"])
我有一个 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"])