在 ruamel from_yaml 中使用 construct_undefined
using construct_undefined in ruamel from_yaml
我正在创建自定义 yaml 标签 MyTag。它可以包含任何给定的有效 yaml - 映射、标量、锚点、序列等。
我如何实现 class MyTag 来为这个标签建模,以便 ruamel 以与解析任何给定 yaml 完全相同的方式解析 !mytag
的内容? MyTag
实例只存储 yaml 内容的任何解析结果。
以下代码有效,断言应该准确地演示它应该做什么,并且它们都通过了。
但我不确定它是否出于正确的原因起作用。 . .特别是在 from_yaml
class 方法中,使用 commented_obj = constructor.construct_undefined(node)
是实现此目标的推荐方法,并且从产生的生成器中消耗 1 且仅消耗 1 是否正确?这不是偶然的工作吗?
我应该改用 construct_object
或 construct_map
之类的东西吗? . .?我能够找到的示例往往知道它正在构造什么类型,因此可以使用 construct_map
或 construct_sequence
来选择要构造的对象类型。在这种情况下,我实际上想借助 usual/standard ruamel 解析其中可能存在的任何未知类型,并将其存储在自己的类型中。
import ruamel.yaml
from ruamel.yaml.comments import CommentedMap, CommentedSeq, TaggedScalar
class MyTag():
yaml_tag = '!mytag'
def __init__(self, value):
self.value = value
@classmethod
def from_yaml(cls, constructor, node):
commented_obj = constructor.construct_undefined(node)
flag = False
for data in commented_obj:
if flag:
raise AssertionError('should only be 1 thing in generator??')
flag = True
return cls(data)
with open('mytag-sample.yaml') as yaml_file:
yaml_parser = ruamel.yaml.YAML()
yaml_parser.register_class(MyTag)
yaml = yaml_parser.load(yaml_file)
custom_tag_with_list = yaml['root'][0]['arb']['k2']
assert type(custom_tag_with_list) is MyTag
assert type(custom_tag_with_list.value) is CommentedSeq
print(custom_tag_with_list.value)
standard_list = yaml['root'][0]['arb']['k3']
assert type(standard_list) is CommentedSeq
assert standard_list == custom_tag_with_list.value
custom_tag_with_map = yaml['root'][1]['arb']
assert type(custom_tag_with_map) is MyTag
assert type(custom_tag_with_map.value) is CommentedMap
print(custom_tag_with_map.value)
standard_map = yaml['root'][1]['arb_no_tag']
assert type(standard_map) is CommentedMap
assert standard_map == custom_tag_with_map.value
custom_tag_scalar = yaml['root'][2]
assert type(custom_tag_scalar) is MyTag
assert type(custom_tag_scalar.value) is TaggedScalar
standard_tag_scalar = yaml['root'][3]
assert type(standard_tag_scalar) is str
assert standard_tag_scalar == str(custom_tag_scalar.value)
还有一些示例 yaml:
root:
- item: blah
arb:
k1: v1
k2: !mytag
- one
- two
- three-k1: three-v1
three-k2: three-v2
three-k3: 123 # arb comment
three-k4:
- a
- b
- True
k3:
- one
- two
- three-k1: three-v1
three-k2: three-v2
three-k3: 123 # arb comment
three-k4:
- a
- b
- True
- item: argh
arb: !mytag
k1: v1
k2: 123
# blah line 1
# blah line 2
k3:
k31: v31
k32:
- False
- string here
- 321
arb_no_tag:
k1: v1
k2: 123
# blah line 1
# blah line 2
k3:
k31: v31
k32:
- False
- string here
- 321
- !mytag plain scalar
- plain scalar
- item: no comment
arb:
- one1
- two2
在 YAML 中,您可以拥有锚点和别名,让对象成为其自身的子对象(使用别名)是完全没问题的。如果要转储 Python 数据结构 data
:
data = [1, 2, 4, dict(a=42)]
data[3]['b'] = data
它转储到:
&id001
- 1
- 2
- 4
- a: 42
b: *id001
为此,锚点和别名是必需的。
加载这样的构造时,ruamel.yaml 递归到嵌套数据结构中,但是如果顶层节点没有导致构造可以引用锚点的真实对象,则递归叶无法解析别名。
为了解决这个问题,使用了生成器,但标量值除外。它首先创建一个空对象,然后递归并更新它的值。在调用构造函数的代码中,检查生成器是否被 returned,在这种情况下,next()
是对数据完成的,并且潜在的 self-recursion “已解决”。
因为你调用construct_undefined()
,你总能得到一个发电机。实际上,如果该方法检测到标量节点(当然不能递归),它可以 return 一个值,但它没有。如果可以,您的代码将无法加载以下 YAML 文档:
!mytag 1
无需修改即可测试您是否获得了生成器,正如在 ruamel.yaml 中调用各种构造函数的代码中所做的那样,它可以同时处理 construct_undefined
和例如construct_yaml_int
(不是生成器)。
我正在创建自定义 yaml 标签 MyTag。它可以包含任何给定的有效 yaml - 映射、标量、锚点、序列等。
我如何实现 class MyTag 来为这个标签建模,以便 ruamel 以与解析任何给定 yaml 完全相同的方式解析 !mytag
的内容? MyTag
实例只存储 yaml 内容的任何解析结果。
以下代码有效,断言应该准确地演示它应该做什么,并且它们都通过了。
但我不确定它是否出于正确的原因起作用。 . .特别是在 from_yaml
class 方法中,使用 commented_obj = constructor.construct_undefined(node)
是实现此目标的推荐方法,并且从产生的生成器中消耗 1 且仅消耗 1 是否正确?这不是偶然的工作吗?
我应该改用 construct_object
或 construct_map
之类的东西吗? . .?我能够找到的示例往往知道它正在构造什么类型,因此可以使用 construct_map
或 construct_sequence
来选择要构造的对象类型。在这种情况下,我实际上想借助 usual/standard ruamel 解析其中可能存在的任何未知类型,并将其存储在自己的类型中。
import ruamel.yaml
from ruamel.yaml.comments import CommentedMap, CommentedSeq, TaggedScalar
class MyTag():
yaml_tag = '!mytag'
def __init__(self, value):
self.value = value
@classmethod
def from_yaml(cls, constructor, node):
commented_obj = constructor.construct_undefined(node)
flag = False
for data in commented_obj:
if flag:
raise AssertionError('should only be 1 thing in generator??')
flag = True
return cls(data)
with open('mytag-sample.yaml') as yaml_file:
yaml_parser = ruamel.yaml.YAML()
yaml_parser.register_class(MyTag)
yaml = yaml_parser.load(yaml_file)
custom_tag_with_list = yaml['root'][0]['arb']['k2']
assert type(custom_tag_with_list) is MyTag
assert type(custom_tag_with_list.value) is CommentedSeq
print(custom_tag_with_list.value)
standard_list = yaml['root'][0]['arb']['k3']
assert type(standard_list) is CommentedSeq
assert standard_list == custom_tag_with_list.value
custom_tag_with_map = yaml['root'][1]['arb']
assert type(custom_tag_with_map) is MyTag
assert type(custom_tag_with_map.value) is CommentedMap
print(custom_tag_with_map.value)
standard_map = yaml['root'][1]['arb_no_tag']
assert type(standard_map) is CommentedMap
assert standard_map == custom_tag_with_map.value
custom_tag_scalar = yaml['root'][2]
assert type(custom_tag_scalar) is MyTag
assert type(custom_tag_scalar.value) is TaggedScalar
standard_tag_scalar = yaml['root'][3]
assert type(standard_tag_scalar) is str
assert standard_tag_scalar == str(custom_tag_scalar.value)
还有一些示例 yaml:
root:
- item: blah
arb:
k1: v1
k2: !mytag
- one
- two
- three-k1: three-v1
three-k2: three-v2
three-k3: 123 # arb comment
three-k4:
- a
- b
- True
k3:
- one
- two
- three-k1: three-v1
three-k2: three-v2
three-k3: 123 # arb comment
three-k4:
- a
- b
- True
- item: argh
arb: !mytag
k1: v1
k2: 123
# blah line 1
# blah line 2
k3:
k31: v31
k32:
- False
- string here
- 321
arb_no_tag:
k1: v1
k2: 123
# blah line 1
# blah line 2
k3:
k31: v31
k32:
- False
- string here
- 321
- !mytag plain scalar
- plain scalar
- item: no comment
arb:
- one1
- two2
在 YAML 中,您可以拥有锚点和别名,让对象成为其自身的子对象(使用别名)是完全没问题的。如果要转储 Python 数据结构 data
:
data = [1, 2, 4, dict(a=42)]
data[3]['b'] = data
它转储到:
&id001
- 1
- 2
- 4
- a: 42
b: *id001
为此,锚点和别名是必需的。
加载这样的构造时,ruamel.yaml 递归到嵌套数据结构中,但是如果顶层节点没有导致构造可以引用锚点的真实对象,则递归叶无法解析别名。
为了解决这个问题,使用了生成器,但标量值除外。它首先创建一个空对象,然后递归并更新它的值。在调用构造函数的代码中,检查生成器是否被 returned,在这种情况下,next()
是对数据完成的,并且潜在的 self-recursion “已解决”。
因为你调用construct_undefined()
,你总能得到一个发电机。实际上,如果该方法检测到标量节点(当然不能递归),它可以 return 一个值,但它没有。如果可以,您的代码将无法加载以下 YAML 文档:
!mytag 1
无需修改即可测试您是否获得了生成器,正如在 ruamel.yaml 中调用各种构造函数的代码中所做的那样,它可以同时处理 construct_undefined
和例如construct_yaml_int
(不是生成器)。