【问题标题】:Removing all blank lines but not comments in Ruamel.yaml删除所有空行但不删除 Ruamel.yaml 中的注释
【发布时间】:2017-07-31 05:23:35
【问题描述】:

我正在使用 Ruamel Python 库以编程方式更新人工编辑的 YAML 文件。

我有这样的数据:

---
a:
  b: '1'
  c: "2"

  d: 3
  # Comment.
  e: 4

我事先不知道 cmets 会在哪里,空行会在哪里。

我需要将其重置为:

---
a:
  b: '1'   
  c: "2"
  d: 3
  # Comment.
  e: 4

我可以从previousanswers 看到如何简单地删除所有 cmets,但我不知道如何查看 CommentToken 内部是否包含我需要保留的评论。

【问题讨论】:

    标签: ruamel.yaml


    【解决方案1】:

    早期版本的 ruamel.yaml 不会保留空行,但是通过在所有 cmets 发出时通过剥离换行符来恢复这种行为相对容易:Emitter.write_comment() in ruamel/yaml/emitter.py .幸运的是,由空格后跟换行符组成的行已经简化为换行符。本质上,您无需在数据中搜索附加的 cmets 并弄清楚如何重写它们,而是让 cmets 来找您。

    我添加了一些空的注释行案例来测试功能:

    import sys
    import ruamel.yaml
    
    yaml_str = """\
    ---
    a:
      b: '1'
      # comment followed by empty lines
    
    
      c: "2"
    
      d: 3
      # Comment.
      e: 4
    
    
      # empty lines followed by comment
      f: 5
    
      # comment between empty lines
    
      g: |+
        an empty line within a multi-line literal
    
        with a trailing empty line that is not stripped
    
      h: 6
    
    # final top level comment
    """
    
    # rename the comment writer
    ruamel.yaml.emitter.Emitter.write_comment_org = ruamel.yaml.emitter.Emitter.write_comment
    
    
    # define your own comment writer that calls the orginal if the comment is not empty
    def strip_empty_lines_write_comment(self, comment):
        # print('{:02d} {:02d} {!r}'.format(self.column, comment.start_mark.column, comment.value))
        comment.value = comment.value.replace('\n', '')
        if comment.value:
            self.write_comment_org(comment)
    
    # install
    ruamel.yaml.emitter.Emitter.write_comment = strip_empty_lines_write_comment
    
    data = ruamel.yaml.round_trip_load(yaml_str, preserve_quotes=True)
    ruamel.yaml.round_trip_dump(data, sys.stdout)
    

    给予:

    a:
      b: '1'
      # comment followed by empty lines
      c: "2"
      d: 3
      # Comment.
      e: 4
      # empty lines followed by comment
      f: 5
      # comment between empty lines
      g: |+
        an empty line within a multi-line literal
    
        with a trailing empty line that is not stripped
    
      h: 6
    # final top level comment
    

    这当然会影响“安装”strip_empty_lines_write_comment 后的所有转储数据。如果您在程序中还需要转储数据 with 空行,那么您需要基于 Emitter 子类化 StrippingEmitter 并创建一个 StrippingRoundTripDumper(如 RoundTripDumper in ruamel/yaml/dumper.py ) 使用该子类。

    (当然也可以去掉代码中注释掉的调试打印语句)

    【讨论】:

    • 一如既往地感谢您的帮助。不幸的是,我不能这样做,因为有时我确实需要保留空白行。我实际上遇到的问题是,当我将数据从一个 YAML 文件复制到另一个文件时,Ruamel.yaml 正在添加我在某些情况下不想要的空行,我不明白为什么。无论如何,我需要能够控制我的工具正在更改的数据的 cmets 和间距,但不更改不使用我的工具的人放置在那里的格式。
    • 对 cme​​ts 的通用控制并非易事,并且会打开另一个蠕虫罐。您是否允许您的工具用户发表评论?如果您不保留这些 cmets,或者不想保留这些 cmets,则可以在用户更新数据上从原始的 cmets 进行修补,这相对容易。
    【解决方案2】:

    它并没有像我问的那样专门解决问题,但是对于它的价值,我最终得到了这个:

    data = ruamel.yaml.round_trip_load(yaml_str, preserve_quotes=True)
    
    space, no_space = map(lambda x:
        [None, None, ruamel.yaml.tokens.CommentToken(x, \
            ruamel.yaml.error.CommentMark(0), None), None], ['\n\n', '\n'])
    
    for key in data['a'].ca.items:
        data['a'].ca.items[key] = no_space
    
    last = data['a'].keys()[-1]
    data['a'].ca.items[last] = space
    

    即我现在只是放弃保留任何非空间 cmets。

    【讨论】:

      【解决方案3】:

      FWIW,我最终得到了以下内容。这将删除任何在剥离时为空的注释,即只有空格。包含任何实际内容的评论将被保留。

      import ruamel.yaml
      
      def monkeypatch_emitter():
          ruamel.yaml.emitter.Emitter.old_write_comment = ruamel.yaml.emitter.Emitter.write_comment
      
          def write_comment(self, comment, *args, **kwargs):
              if comment.value.strip():
                  self.old_write_comment(comment, *args, **kwargs)
      
          ruamel.yaml.emitter.Emitter.write_comment = write_comment
      
      def main():
          yaml = ruamel.yaml.YAML()
          # do yaml stuff here
      
      if __name__ == '__main__:
          monkeypatch_emitter()
          main()
      

      自从添加上述答案后,write_comment 签名可能已经发生了一些变化,因为它由于偶尔存在“pre”关键字参数而失败。如果将来再次更改,上面的代码应该传递任何额外的参数。

      【讨论】:

        猜你喜欢
        • 2016-05-21
        • 2013-06-22
        • 1970-01-01
        • 2016-02-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-11-30
        • 2014-08-10
        相关资源
        最近更新 更多