yaml 使用键或父键作为值
yaml use key or parent key as value
我刚开始使用 YAML(通过 pyyaml),我想知道是否有任何方法可以声明键的值是键名本身或父键。
例如
---
foo: &FOO
bar: !.
baz: !..
foo2:
<<: *FOO
…
{‘foo’: {‘bar’: ‘bar’, ‘baz’: ’foo’}, ‘foo2’:{‘bar’:’bar’, ‘baz’:’foo2’}}
(分别注意 bar 和 baz 上的点和双点——它们只是用于获取键名和父键名的占位符)
我试过使用 add_constructor
:
def key_construct(loader, node):
# return the key here
pass
yaml.add_constructor('!.', key_construct)
但是 Node 没有保存密钥(或对父项的引用),我找不到获取它们的方法。
编辑:
所以,这是我的真实用例和基于 Anthon 响应的解决方案:
我有一个记录器配置文件(在 yaml 中),我想在那里重用一些定义:
handlers:
base: &base_handler
(): globals.TimedRotatingFileHandlerFactory
name: ../
when: midnight
backupCount: 14
level: DEBUG
formatter: generic
syslog:
class: logging.handlers.SysLogHandler
address: ['localhost', 514]
facility: local5
level: NOTSET
formatter: syslog
access:
<<: *base_handler
error:
<<: *base_handler
loggers:
base: &base_logger
handlers: [../, syslog]
qualname: ../
access:
<<: *base_logger
error:
<<: *base_logger
handlers: [../, syslog, email]
Anthon 建议的解决方案是在处理后遍历配置字典:
def expand_yaml(val, parent=None, key=None, key1=None, key2=None):
if isinstance(val, str):
if val == './':
parent[key] = key1
elif val == '../':
parent[key] = key2
elif isinstance(val, dict):
for k, v in val.items():
expand_yaml(v, val, k, k, key1)
elif isinstance(val, list):
parent[key] = val[:] # support inheritance of the list (as in *base_logger)
for index, e in enumerate(parent[key]):
expand_yaml(e, parent[key], index, key1, key2)
return val
您在构建元素时没有太多上下文,因此您不会找到您的键,当然也不会找到父键来填充值,而无需挖掘调用堆栈上下文(loader
知道 foo
、bar
和 baz
,但无法确定哪个是相应的键或 parent_key) .
我建议你做的是用 key_construct
创建一个你 return 的特殊节点,然后在 YAML 加载之后,遍历你的 yaml.load()
return编辑。除非你有其他 !
对象,这使得遍历结果组合比 sequences/lists 和 mappings/dicts 的纯组合更难 ¹:
import ruamel.yaml as yaml
yaml_str = """\
foo: &FOO
bar: !.
baz: !..
foo2:
<<: *FOO
"""
class Expander:
def __init__(self, tag):
self.tag = tag
def expand(self, key, parent_key):
if self.tag == '!.':
return key
elif self.tag == '!..':
return parent_key
raise NotImplementedError
def __repr__(self):
return "E({})".format(self.tag)
def expand(d, key=None, parent_key=None):
if isinstance(d, list):
for elem in d:
expand(elem, key=key, parent_key=parent_key)
elif isinstance(d, dict):
for k in d:
v = d[k]
if isinstance(v, Expander):
d[k] = v.expand(k, parent_key)
expand(d[k], key, parent_key=k)
return d
def key_construct(loader, node):
return Expander(node.tag)
yaml.add_constructor('!.', key_construct)
yaml.add_constructor('!..', key_construct)
data = yaml.load(yaml_str)
print(data)
print(expand(data))
给你:
{'foo': {'bar': E(!.), 'baz': E(!..)}, 'foo2': {'bar': E(!.), 'baz': E(!..)}}
{'foo': {'bar': 'bar', 'baz': 'foo'}, 'foo2': {'bar': 'bar', 'baz': 'foo2'}}
¹ 这是使用 ruamel.yaml 完成的,我是作者。 PyYAML,其中 ruamel.yaml 是一个功能超集,应该工作相同。
我刚开始使用 YAML(通过 pyyaml),我想知道是否有任何方法可以声明键的值是键名本身或父键。 例如
---
foo: &FOO
bar: !.
baz: !..
foo2:
<<: *FOO
…
{‘foo’: {‘bar’: ‘bar’, ‘baz’: ’foo’}, ‘foo2’:{‘bar’:’bar’, ‘baz’:’foo2’}}
(分别注意 bar 和 baz 上的点和双点——它们只是用于获取键名和父键名的占位符)
我试过使用 add_constructor
:
def key_construct(loader, node):
# return the key here
pass
yaml.add_constructor('!.', key_construct)
但是 Node 没有保存密钥(或对父项的引用),我找不到获取它们的方法。
编辑:
所以,这是我的真实用例和基于 Anthon 响应的解决方案: 我有一个记录器配置文件(在 yaml 中),我想在那里重用一些定义:
handlers:
base: &base_handler
(): globals.TimedRotatingFileHandlerFactory
name: ../
when: midnight
backupCount: 14
level: DEBUG
formatter: generic
syslog:
class: logging.handlers.SysLogHandler
address: ['localhost', 514]
facility: local5
level: NOTSET
formatter: syslog
access:
<<: *base_handler
error:
<<: *base_handler
loggers:
base: &base_logger
handlers: [../, syslog]
qualname: ../
access:
<<: *base_logger
error:
<<: *base_logger
handlers: [../, syslog, email]
Anthon 建议的解决方案是在处理后遍历配置字典:
def expand_yaml(val, parent=None, key=None, key1=None, key2=None):
if isinstance(val, str):
if val == './':
parent[key] = key1
elif val == '../':
parent[key] = key2
elif isinstance(val, dict):
for k, v in val.items():
expand_yaml(v, val, k, k, key1)
elif isinstance(val, list):
parent[key] = val[:] # support inheritance of the list (as in *base_logger)
for index, e in enumerate(parent[key]):
expand_yaml(e, parent[key], index, key1, key2)
return val
您在构建元素时没有太多上下文,因此您不会找到您的键,当然也不会找到父键来填充值,而无需挖掘调用堆栈上下文(loader
知道 foo
、bar
和 baz
,但无法确定哪个是相应的键或 parent_key) .
我建议你做的是用 key_construct
创建一个你 return 的特殊节点,然后在 YAML 加载之后,遍历你的 yaml.load()
return编辑。除非你有其他 !
对象,这使得遍历结果组合比 sequences/lists 和 mappings/dicts 的纯组合更难 ¹:
import ruamel.yaml as yaml
yaml_str = """\
foo: &FOO
bar: !.
baz: !..
foo2:
<<: *FOO
"""
class Expander:
def __init__(self, tag):
self.tag = tag
def expand(self, key, parent_key):
if self.tag == '!.':
return key
elif self.tag == '!..':
return parent_key
raise NotImplementedError
def __repr__(self):
return "E({})".format(self.tag)
def expand(d, key=None, parent_key=None):
if isinstance(d, list):
for elem in d:
expand(elem, key=key, parent_key=parent_key)
elif isinstance(d, dict):
for k in d:
v = d[k]
if isinstance(v, Expander):
d[k] = v.expand(k, parent_key)
expand(d[k], key, parent_key=k)
return d
def key_construct(loader, node):
return Expander(node.tag)
yaml.add_constructor('!.', key_construct)
yaml.add_constructor('!..', key_construct)
data = yaml.load(yaml_str)
print(data)
print(expand(data))
给你:
{'foo': {'bar': E(!.), 'baz': E(!..)}, 'foo2': {'bar': E(!.), 'baz': E(!..)}}
{'foo': {'bar': 'bar', 'baz': 'foo'}, 'foo2': {'bar': 'bar', 'baz': 'foo2'}}
¹ 这是使用 ruamel.yaml 完成的,我是作者。 PyYAML,其中 ruamel.yaml 是一个功能超集,应该工作相同。