You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将PrettyDumper与字符串表示器结合生成合规PyYAML输出?

解决PyYAML缩进与多行字符串格式兼容问题

问题场景

使用PyYAML 6.0 + Python 3.9,需完成以下操作:

  • 创建YAML列表
  • 将该列表作为多行字符串嵌入另一个YAML对象
  • 在现有文档中替换该YAML对象
  • 输出符合YAML 1.2规范的文档

现有实现存在矛盾问题:

  1. 仅启用多行字符串格式化时,列表项缩进不符合要求,触发仓库预提交检查的自动格式调整
  2. 仅使用自定义缩进的PrettyDumper时,多行字符串格式失效,变成带转义符的单行字符串

错误合并方案问题点

之前合并str_presenter与PrettyDumper时,错误地将字符串格式化逻辑注册到全局PyYAML,而非自定义Dumper实例内部,导致多行字符串规则未被正确调用。

正确实现代码

import yaml.emitter
import yaml.serializer
import yaml.representer
import yaml.resolver


class IndentingEmitter(yaml.emitter.Emitter):
    def increase_indent(self, flow=False, indentless=False):
        """确保列表项始终缩进"""
        return super().increase_indent(
            flow=False,
            indentless=False,
        )


class PrettyDumper(
    IndentingEmitter,
    yaml.serializer.Serializer,
    yaml.representer.Representer,
    yaml.resolver.Resolver,
):
    def __init__(
        self,
        stream,
        default_style=None,
        default_flow_style=False,
        canonical=None,
        indent=2,  # 显式指定缩进为2,符合YAML 1.2规范
        width=None,
        allow_unicode=None,
        line_break=None,
        encoding=None,
        explicit_start=None,
        explicit_end=None,
        version=None,
        tags=None,
        sort_keys=True,
    ):
        IndentingEmitter.__init__(
            self,
            stream,
            canonical=canonical,
            indent=indent,
            width=width,
            allow_unicode=allow_unicode,
            line_break=line_break,
        )
        yaml.serializer.Serializer.__init__(
            self,
            encoding=encoding,
            explicit_start=explicit_start,
            explicit_end=explicit_end,
            version=version,
            tags=tags,
        )
        yaml.representer.Representer.__init__(
            self,
            default_style=default_style,
            default_flow_style=default_flow_style,
            sort_keys=sort_keys,
        )
        yaml.resolver.Resolver.__init__(self)
        
        # 在当前Dumper实例注册字符串格式化逻辑,而非全局注册
        self.add_representer(str, self.str_presenter)
        # 兼容安全序列化场景
        self.add_representer(str, self.str_presenter, yaml.representer.SafeRepresenter)

    def str_presenter(self, dumper, data):
        """配置多行字符串使用|样式格式化"""
        if data.count('\n') > 0:
            return dumper.represent_scalar('tag:yaml.org,2002:str', data, style='|')
        return dumper.represent_scalar('tag:yaml.org,2002:str', data)


# 修改业务类,使用自定义PrettyDumper
class DoYamlStuff:
    @staticmethod
    def post_renderers(images):
        return yaml.dump([
            {
                "op": "replace",
                "path": "/spec/postRenderers",
                "value": [
                    {
                        "kustomize": {
                            "images": images
                        }
                    }
                ]
            }], Dumper=PrettyDumper)

    @classmethod
    def images_patch(cls, chart, images, ecr_url):
        return {
            "target": {
                "kind": "HelmRelease",
                "name": chart,
                "namespace": chart
            },
            "patch": cls.post_renderers([x.patch(ecr_url) for x in images])
        }


# 测试输出
if __name__ == "__main__":
    # 模拟Image对象
    class MockImage:
        def __init__(self, name):
            self.name = name
        def patch(self, ecr_url):
            return {
                "name": f"{self.name}:latest",
                "newName": f"{ecr_url}/{self.name}",
                "newTag": "latest"
            }
    
    patches = [DoYamlStuff.images_patch("nginx", [MockImage("nginx")], "12345678910.dkr.ecr.eu-west-1.amazonaws.com")]
    print(yaml.dump(patches, Dumper=PrettyDumper))

正确输出示例

- patch: |
    - op: replace
      path: /spec/postRenderers
      value:
        - kustomize:
            images:
              - name: nginx:latest
                newName: 12345678910.dkr.ecr.eu-west-1.amazonaws.com/nginx
                newTag: latest
  target:
    kind: HelmRelease
    name: nginx
    namespace: nginx

关键修复点

  1. 缩进控制:通过IndentingEmitter强制列表项缩进,同时显式指定indent=2,严格匹配YAML 1.2规范
  2. 多行字符串逻辑:将str_presenter注册到自定义PrettyDumper实例内部,确保缩进与多行字符串规则同时生效
  3. 兼容安全序列化:同时将格式化逻辑注册到SafeRepresenter,避免安全模式下格式失效

内容的提问来源于stack exchange,提问作者turbonerd

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 01:22:20