ruamel from_yaml에서 construct_undefined 사용
사용자 지정 yaml 태그 MyTag를 만들고 있습니다. 지정된 유효한 yaml (맵, 스칼라, 앵커, 시퀀스 등)을 포함 할 수 있습니다.
ruamel이 !mytag주어진 yaml을 구문 분석하는 것과 똑같은 방식으로 a의 내용을 구문 분석하도록이 태그를 모델링하기 위해 MyTag 클래스를 어떻게 구현 합니까? MyTagYAML 내용의 구문 분석 된 결과는 어떤 인스턴스 만 저장합니다.
다음 코드는 작동하며 어설 션 은 수행 해야하는 작업을 정확히 보여 주어야 하며 모두 통과해야합니다.
그러나 그것이 올바른 이유로 작동하는지 확실하지 않습니다. . . 특히 from_yaml클래스 메서드에서 commented_obj = constructor.construct_undefined(node)이를 달성하기 위해 권장되는 방법을 사용 하고 있으며 산출 된 생성기에서 1 개만 소비하고 있습니까? 우연히 작동하지 않습니까?
대신 construct_object, 또는 construct_map또는 같은 것을 사용해야 합니다. . .? 나는 그것을 건설하고 입력 한 내용을 알고하는 경향이 찾을 수 있었던 예는, 그래서 중 하나를 사용하는 것 construct_map또는 construct_sequence구조에 오브젝트의 유형을 선택합니다. 이 경우에는 알 수없는 유형에 대해 일반적인 / 표준 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은 중첩 된 데이터 구조로 반복되지만 최상위 노드가 앵커를 참조 할 수있는 실제 객체를 생성하지 않은 경우 순환 리프는 별칭을 확인할 수 없습니다.
이를 해결하기 위해 스칼라 값을 제외하고 생성기가 사용됩니다. 먼저 빈 개체를 만든 다음 반복하여 값을 업데이트합니다. 생성자를 호출하는 코드에서 생성자가 반환되는지 확인하고이 경우 next()데이터에 대해 수행되고 잠재적 인 자체 재귀가 "해결"되었는지 확인합니다.
를 호출하기 때문에 construct_undefined()항상 발전기를 얻습니다. 실제로이 메서드는 스칼라 노드 (물론 재귀 할 수 없음)를 감지하면 값을 반환 할 수 있지만 그렇지 않습니다. 그렇다면 코드는 다음 YAML 문서를로드 할 수 없습니다.
!mytag 1
당신이 발전기를 얻을 아닌지 테스트하는 것이 수정없이, 등이 모두 처리 할 수 있도록 다양한 생성자를 호출 ruamel.yaml의 코드에서 수행되는 construct_undefined예와 construct_yaml_int(발전기하지 않은).