簡體   English   中英

ruamel.yaml 用於自定義標簽的自定義 CommentedMapping

[英]ruamel.yaml custom CommentedMapping for custom tags

我有一個帶有自定義標簽的 YAML 文件,如下所示:

flow123d_version: 3.1.0
problem: !modulek.Coupling_Sequential
  description: Simple dual porosity test - steady flow, simple transport
  mesh:
    mesh_file: ../00_mesh/square_1x1_40el.msh
  flow_equation: !Flow_Darcy_MH
    nonlinear_solver:
      linear_solver: !Petsc
        a_tol: 1.0e-07

到目前為止,我的代碼可以加載它並將其轉儲回來。 我的問題是我希望能夠檢查每個自定義! 並檢查其他文件是否正確。 讓我們看看我的文件的第二行。 你可以看到我的第一個自定義標簽,它由 module.class_name 組成,我需要檢查它們。 我想將“modulek”解析為模塊,將“Coupling_Sequential”解析為 class_name。 我的代碼看起來像這樣。

import types
import re

import ruamel.yaml as ruml
from ruamel.yaml.comments import CommentedMap, CommentedSeq

CommentsTag = ruml.comments.Tag


class CommentedScalar:
    """
    Class to store all scalars with their tags
    """
    original_constructors = {}

    def __str__(self):
        return str(self.value)

    def __repr__(self):
        return str(self.value)

    @classmethod
    def to_yaml(cls, dumper, data):
        representer = dumper.yaml_representers[type(data.value).__mro__[0]]
        node = representer(dumper, data.value)
        if data.tag.value is None:
            tag = node.tag
        elif data.tag.value.startswith(u'tag:yaml.org,2002'):
            tag = node.tag
        else:
            tag = data.tag.value
        # print("val: ", data.value, "repr: ", node.value, "tag: ", tag)
        return dumper.represent_scalar(tag, node.value)

    def __init__(self, tag, value):
        complete_tag = tag.split('.')
        self.tag.value = tag
        self.value = value
        # self.module.value = complete_tag
        self.module = '.'.join(complete_tag[:len(complete_tag) - 1])
        self.class_name = complete_tag[-1]

    @property
    def tag(self):
        # type: () -> Any
        if not hasattr(self, CommentsTag.attrib):
            setattr(self, CommentsTag.attrib, CommentsTag())
        return getattr(self, CommentsTag.attrib)

def construct_any_tag(self, tag_suffix, node):
    if tag_suffix is None:
        orig_tag = None
    else:
        orig_tag = "!" + tag_suffix
    if isinstance(node, ruml.ScalarNode):

        implicit_tag = self.composer.resolver.resolve(ruml.ScalarNode, node.value, (True, None))
        if implicit_tag in self.yaml_constructors:
            # constructor = CommentedScalar.original_constructors[implicit_tag]
            constructor = self.yaml_constructors[implicit_tag]
        else:
            constructor = self.construct_undefined

        data = constructor(self, node)
        if isinstance(data, types.GeneratorType):
            generator = data
            data = next(generator)  # type: ignore

        scal = CommentedScalar(orig_tag, data)
        yield scal

    elif isinstance(node, ruml.SequenceNode):
        for seq in self.construct_yaml_seq(node):
            seq.yaml_set_tag(orig_tag)
            yield seq
    elif isinstance(node, ruml.MappingNode):
        for map in self.construct_yaml_map(node):
            map.yaml_set_tag(orig_tag)
            yield map
    else:
        for dummy in self.construct_undefined(node):
            yield dummy


def represent_commented_seq(cls, data):
    if data.tag.value is None:
        tag = u'tag:yaml.org,2002:seq'
    else:
        tag = data.tag.value
    return cls.represent_sequence(tag, data)

def get_yaml_serializer():
    """
    Get YAML serialization/deserialization object with proper
    configuration for conversion.
    :return: Confugured instance of ruamel.yaml.YAML.
    """
    yml = ruml.YAML(typ='rt')
    yml.indent(mapping=2, sequence=4, offset=2)
    yml.width = 120
    yml.representer.add_representer(CommentedScalar, CommentedScalar.to_yaml)
    yml.representer.add_representer(CommentedSeq, represent_commented_seq)
    yml.representer.add_representer(CommentedMap, CommentedMapping.to_yaml)
    yml.constructor.add_multi_constructor("!", construct_any_tag)
    return yml


def get_node_tag(node):
    if hasattr(node, "tag"):
        tag = node.tag.value
        if tag and len(tag) > 1 and tag[0] == '!' and tag[1] != '!':
            return tag
    return ""


def load_yaml(path):
    yml = get_yaml_serializer()
    with open(path, 'r') as stream:
        data = yml.load(stream)
    return data


def write_yaml(data, path):
    yml = get_yaml_serializer()
    with open(path, 'w')as stream:
        yml.dump(data, stream)

我正在考慮編寫類似於 CommentedScalar 的“CommentedMapping”,但我被困在這里並且找不到任何工作。

@classmethod
def to_yaml(cls, dumper, data):
    ...
    ...
    return ???

概括
如果有人將我推向正確的方向,我會很高興。 我什至不知道這是否是正確的方法。

YAML 輸入中所有顯式標記的節點都是映射節點,因此永遠不會創建您的CommentedScalar (使用此輸入)。

由於往返模式下的 ruamel.yaml 已經可以加載和轉儲您的 YAML,因此您最好只遍歷加載的數據並檢查標簽屬性。

然而,它是可能加載這些映射節點,但不使用yield在你的方式(而你只需要yield為復雜節點(映射,序列)不是簡單的節點)。

import sys
import ruamel.yaml

yaml_str = """\
flow123d_version: 3.1.0
problem: !modulek.Coupling_Sequential
  description: Simple dual porosity test - steady flow, simple transport
  mesh:
    mesh_file: ../00_mesh/square_1x1_40el.msh
  flow_equation: !Flow_Darcy_MH
    nonlinear_solver:
      linear_solver: !Petsc
        a_tol: 1.0e-07
"""

yaml = ruamel.yaml.YAML()

@yaml.register_class
class MyMap(ruamel.yaml.comments.CommentedMap):
    def __init__(self, tag):
        ruamel.yaml.comments.CommentedMap.__init__(self)
        self._tag = tag + "@@"  # just to make clear things were changed here

    @classmethod
    def to_yaml(cls, representer, data):
        return representer.represent_mapping(data._tag, data)


def construct_any_tag(self, tag_suffix, node):
    if tag_suffix is None:
        orig_tag = None
    else:
        orig_tag = "!" + tag_suffix
    if isinstance(node, ruamel.yaml.nodes.MappingNode):
        data = MyMap(orig_tag)
        yield data
        state = ruamel.yaml.constructor.SafeConstructor.construct_mapping(self, node, deep=True)
        data.update(state)
    else:
        raise NotImplementedError


yaml = ruamel.yaml.YAML()
yaml.constructor.add_multi_constructor("!", construct_any_tag)
data = yaml.load(yaml_str)
yaml.dump(data, sys.stdout)

這使:

flow123d_version: 3.1.0
problem: !modulek.Coupling_Sequential@@
  description: Simple dual porosity test - steady flow, simple transport
  mesh:
    mesh_file: ../00_mesh/square_1x1_40el.msh
  flow_equation: !Flow_Darcy_MH@@
    nonlinear_solver:
      linear_solver: !Petsc@@
        a_tol: 1.0e-07

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM