ruamel from_yaml'de construct_undefined kullanarak

Oct 16 2020

Özel bir yaml etiketi MyTag oluşturuyorum. Herhangi bir geçerli yaml - harita, skaler, çapa, sıra vb. İçerebilir.

Bu etiketi modellemek için MyTag sınıfını nasıl uygulayabilirim ki ruamel a'nın içeriğini herhangi bir yaml'ı çözümleyeceği !mytagşekilde tam olarak ayrıştırır? MyTagYaml içeriğinin çözümlü sonuç ne olursa olsun örneği sadece saklar.

Aşağıdaki kod çalışıyor ve tam olarak ne göstermelidir gerektiğini iddia etmelidir yapmak ve hepsi pas.

Ama doğru nedenlerle işe yarayıp yaramadığından emin değilim. . . Özellikle from_yamlsınıf yönteminde, commented_obj = constructor.construct_undefined(node)bunu başarmak için önerilen bir yol kullanılıyor ve elde edilen jeneratörden 1 ve sadece 1 tüketmek doğru mu? Sadece tesadüfen çalışmak değil mi?

Bunun yerine construct_object, veya construct_mapveya gibi bir şey mi kullanmalıyım ? . .? Bulabildiğim örnekler, hangi türden inşa edildiğini bilme eğilimindedir, bu nedenle, hangi tür nesneyi inşa edeceğimi ya kullanır construct_mapya construct_sequenceda seçerdi. Bu durumda, olağan / standart ruamel ayrıştırmasını, orada olabilecek bilinmeyen türler için etkili bir şekilde geri almak istiyorum ve sadece kendi türünde saklamak istiyorum.

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)

Ve bazı örnek 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

Yanıtlar

Anthon Oct 16 2020 at 04:23

YAML'de çapalara ve takma adlara sahip olabilirsiniz ve bir nesnenin kendisinin çocuğu olması (bir takma ad kullanarak) tamamen iyidir. Python veri yapısını dökmek istiyorsanız data:

data = [1, 2, 4, dict(a=42)]
data[3]['b'] = data

dökülür:

&id001
- 1
- 2
- 4
- a: 42
  b: *id001

ve bunun için çapalar ve takma adlar gereklidir.

Böyle bir yapı yüklenirken, ruamel.yaml, iç içe geçmiş veri yapılarına yinelenir, ancak üst düzey düğüm, çapanın referans yapılabileceği gerçek bir nesnenin oluşturulmasına neden olmadıysa, yinelemeli yaprak takma adı çözemez.

Bunu çözmek için skaler değerler dışında bir jeneratör kullanılır. Önce boş bir nesne yaratır, sonra tekrar eder ve değerlerini günceller. Oluşturucuyu çağıran kodda, bir jeneratörün döndürülüp döndürülmediğini görmek için bir kontrol yapılır ve bu durumda next()veriler üzerinde yapılır ve potansiyel kendi kendine özyineleme "çözülür".

Çünkü aradığın için construct_undefined()her zaman bir jeneratör alırsın. Pratik olarak bu yöntem, skaler bir düğümü (elbette tekrarlayamayan) tespit ederse bir değer döndürebilir, ancak yapmaz. Eğer öyleyse, kodunuz aşağıdaki YAML belgesini yükleyemez:

!mytag 1

ruamel.yaml'deki kodda yapıldığı gibi, bir jeneratör alıp almadığınızı test eden modifikasyonlar olmadan, çeşitli kurucuları çağırarak hem construct_undefinedve hem de örneğin construct_yaml_int(bu bir jeneratör değildir).