YAML错误:无法确定标记的构造函数

Mat*_*ton 7 aws-cloudformation ruamel.yaml

这与问题/ 44786412非常相似,但我的似乎是由YAML safe_load()触发的.我正在使用Ruamel的库和YamlReader将一堆CloudFormation部分粘合到一个合并的模板中.砰砰声是不是正确的YAML?

Outputs:
  Vpc:
    Value: !Ref vpc
    Export:
      Name: !Sub "${AWS::StackName}-Vpc"
Run Code Online (Sandbox Code Playgroud)

没问题

Outputs:
  Vpc:
    Value:
      Ref: vpc
    Export:
      Name:
        Fn::Sub: "${AWS::StackName}-Vpc"

Resources:
  vpc:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock:
        Fn::FindInMap: [ CidrBlock, !Ref "AWS::Region", Vpc ]
Run Code Online (Sandbox Code Playgroud)

第2部分; 如何让load()单独留下'Fn :: Select:'的权利.

  FromPort: 
    Fn::Select: [ 0, Fn::FindInMap: [ Service, https, Ports ] ]
Run Code Online (Sandbox Code Playgroud)

转换成这个,现在CF不喜欢.

  FromPort:
    Fn::Select: [0, {Fn::FindInMap: [Service, https, Ports]}]
Run Code Online (Sandbox Code Playgroud)

如果我完全展开声明,那么没有问题.我想速记只是有问题的.

  FromPort:
    Fn::Select:
    - 0
    - Fn::FindInMap: [Service, ssh, Ports]
Run Code Online (Sandbox Code Playgroud)

Ant*_*hon 5

您的“爆炸符号”是正确的YAML,通常称为标记。如果要与一起使用,则safe_load()必须为!Ref和!Sub标记提供构造函数,例如,使用:

ruamel.yaml.add_constructor(u'!Ref', your_ref_constructor, constructor=ruamel.yaml.SafeConstructor)
Run Code Online (Sandbox Code Playgroud)

对于两个标签,您应该期望在哪里处理标量值。而不是更常见的映射。

我建议您使用RoundTripLoader代替SafeLoader,也可以保留顺序,注释等。该RoundTripLoader是的一个子类SafeLoader。

如果您使用的是支持往返标量的ruamel.yaml> = 0.15.33,则可以这样做(使用新的ruamel.yaml API):

import sys
from ruamel.yaml import YAML

yaml = YAML()
yaml.preserve_quotes = True

data = yaml.load("""\
Outputs:
  Vpc:
    Value: !Ref: vpc    # first tag
    Export:
      Name: !Sub "${AWS::StackName}-Vpc"  # second tag
""")

yaml.dump(data, sys.stdout)
Run Code Online (Sandbox Code Playgroud)

要得到:

Outputs:
  Vpc:
    Value: !Ref: vpc    # first tag
    Export:
      Name: !Sub "${AWS::StackName}-Vpc"  # second tag
Run Code Online (Sandbox Code Playgroud)

在较旧的0.15.X版本中,您必须自己指定标量对象的类。如果您有许多对象,那么会很麻烦,但是它允许其他功能:

import sys
from ruamel.yaml import YAML


class Ref:
    yaml_tag = u'!Ref:'

    def __init__(self, value, style=None):
        self.value = value
        self.style = style

    @classmethod
    def to_yaml(cls, representer, node):
        return representer.represent_scalar(cls.yaml_tag,
                                            u'{.value}'.format(node), node.style)

    @classmethod
    def from_yaml(cls, constructor, node):
        return cls(node.value, node.style)

    def __iadd__(self, v):
        self.value += str(v)
        return self

class Sub:
    yaml_tag = u'!Sub'
    def __init__(self, value, style=None):
        self.value = value
        self.style = style

    @classmethod
    def to_yaml(cls, representer, node):
        return representer.represent_scalar(cls.yaml_tag,
                                            u'{.value}'.format(node), node.style)

    @classmethod
    def from_yaml(cls, constructor, node):
        return cls(node.value, node.style)


yaml = YAML(typ='rt')
yaml.register_class(Ref)
yaml.register_class(Sub)

data = yaml.load("""\
Outputs:
  Vpc:
    Value: !Ref: vpc    # first tag
    Export:
      Name: !Sub "${AWS::StackName}-Vpc"  # second tag
""")

data['Outputs']['Vpc']['Value'] += '123'

yaml.dump(data, sys.stdout)
Run Code Online (Sandbox Code Playgroud)

这使:

Outputs:
  Vpc:
    Value: !Ref: vpc123 # first tag
    Export:
      Name: !Sub "${AWS::StackName}-Vpc"  # second tag
Run Code Online (Sandbox Code Playgroud)