menggunakan construct_undefined di ruamel from_yaml
Saya membuat tag yaml khusus MyTag. Ini dapat berisi yaml valid yang diberikan - peta, skalar, jangkar, urutan, dll.
Bagaimana cara mengimplementasikan kelas MyTag untuk memodelkan tag ini sehingga ruamel mem-parsing konten a !mytagdengan cara yang persis sama seperti yang akan mengurai yaml yang diberikan? The MyTagMisalnya saja toko apapun hasil parsing dari isi YAML adalah.
Kode berikut berfungsi, dan pernyataan harus menunjukkan dengan tepat apa yang harus dilakukan dan semuanya lolos.
Tapi saya tidak yakin apakah itu berfungsi untuk alasan yang benar. . . Khususnya dalam from_yamlmetode kelas, apakah menggunakan commented_obj = constructor.construct_undefined(node)cara yang disarankan untuk mencapai hal ini, dan apakah mengkonsumsi 1 dan hanya 1 dari generator yang dihasilkan benar? Ini tidak hanya bekerja karena kecelakaan?
Haruskah saya menggunakan sesuatu seperti construct_object, atau construct_mapatau. . .? Contoh-contoh yang dapat saya temukan cenderung mengetahui tipe apa yang sedang dibangunnya, jadi akan menggunakan construct_mapatau construct_sequencememilih tipe objek yang akan dibangun. Dalam hal ini saya secara efektif ingin mendukung penguraian ruamel biasa / standar untuk jenis apa pun yang tidak diketahui yang mungkin ada di sana, dan hanya menyimpannya dalam tipenya sendiri.
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)
Dan beberapa contoh 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
Jawaban
Di YAML Anda dapat memiliki jangkar dan alias, dan tidak masalah jika objek menjadi anak itu sendiri (menggunakan alias). Jika Anda ingin membuang struktur data Python data:
data = [1, 2, 4, dict(a=42)]
data[3]['b'] = data
itu dibuang ke:
&id001
- 1
- 2
- 4
- a: 42
b: *id001
dan untuk itu diperlukan jangkar dan alias.
Saat memuat konstruksi seperti itu, ruamel.yaml muncul kembali ke dalam struktur data yang disarangkan, tetapi jika simpul tingkat atas tidak menyebabkan objek nyata yang akan dibangun di mana jangkar dapat dijadikan referensi, daun rekursif tidak dapat menyelesaikan alias.
Untuk mengatasinya, generator digunakan, kecuali nilai skalar. Ini pertama-tama membuat objek kosong, lalu mengulang dan memperbarui nilainya. Dalam pemanggilan kode konstruktor, pemeriksaan dilakukan untuk melihat apakah generator dikembalikan, dan dalam hal next()ini dilakukan pada data, dan potensi rekursi sendiri "diselesaikan".
Karena Anda menelepon construct_undefined(), Anda selalu mendapatkan generator. Praktisnya, metode tersebut dapat mengembalikan nilai jika mendeteksi node skalar (yang tentu saja tidak dapat muncul kembali), tetapi ternyata tidak. Jika ya, kode Anda tidak dapat memuat dokumen YAML berikut:
!mytag 1
tanpa modifikasi yang menguji apakah Anda mendapatkan generator atau tidak, seperti yang dilakukan dalam kode di ruamel.yaml memanggil berbagai konstruktor sehingga dapat menangani keduanya construct_undefineddan misalnya construct_yaml_int(yang bukan generator).