2016-10-01 22 views
1

我正在使用Ruamel Python庫以編程方式編輯人工編輯的YAML文件。在Ruamel.yaml中的某個鍵之前插入註釋

我很努力地瞭解如何將註釋插入到結構化數據中。

我有一些數據:

a: 
    b: banana 
    c: apple 
    d: orange 

我想添加評論和一個新的關鍵:

a: 
    b: banana 
    c: apple 
    d: orange 
    # This is my comment 
    e: pear 

是否有可能做到這一點使用ruamel.yaml,如果是這樣,怎麼樣?

回答

2

是的,這是可能的,因爲你可以通過做一個來回檢查:

import sys 
import ruamel.yaml 

with open('your_input.yaml') as fp: 
    data = ruamel.yaml.round_trip_load(yaml_str) 
ruamel.yaml.round_trip_dump(data, sys.stdout) 

打印的輸出會符合您的輸入,因此在某種程度上註釋插入data層次結構,保存完好,並在傾銷時寫出來。

ruamel.yaml評論連接到包裝類listsdict s,這你print(type(data['a'])檢查:這是一個CommentedMap(從ruamel.yaml.comment.py)。對於屬性_yaml_commenta掛起的價值,你可以通過屬性ca訪問註釋信息:

cm = data['a'] 
print(cm.ca) 

給出:

items={'e': [None, [CommentToken(value='# This is my comment\n')], None, None]}) 

這說明註釋與鍵e相關,即正在評論。不幸的是,CommentToken不能僅僅通過調用它來創建它(CommentToken(value='# This is my comment\n')),它需要更多的工作,因爲它至少需要一個開始Mark

沒有「幫手」程序來創建這樣一個評論,但通過觀察CommentedMap和它的基類CommentedBase你能想出以下¹:

import sys 
import ruamel.yaml 

if not hasattr(ruamel.yaml.comments.CommentedMap, "yaml_set_comment_before_key"): 
    def my_yaml_set_comment_before_key(self, key, comment, column=None, 
             clear=False): 
     """ 
     append comment to list of comment lines before key, '# ' is inserted 
      before the comment 
     column: determines indentation, if not specified take indentation from 
       previous comment, otherwise defaults to 0 
     clear: if True removes any existing comments instead of appending 
     """ 
     key_comment = self.ca.items.setdefault(key, [None, [], None, None]) 
     if clear: 
      key_comment[1] = [] 
     comment_list = key_comment[1] 
     if comment: 
      comment_start = '# ' 
      if comment[-1] == '\n': 
       comment = comment[:-1] # strip final newline if there 
     else: 
      comment_start = '#' 
     if column is None: 
      if comment_list: 
       # if there already are other comments get the column from them 
       column = comment_list[-1].start_mark.column 
      else: 
       column = 0 
     start_mark = ruamel.yaml.error.Mark(None, None, None, column, None, None) 
     comment_list.append(ruamel.yaml.tokens.CommentToken(
      comment_start + comment + '\n', start_mark, None)) 
     return self 

    ruamel.yaml.comments.CommentedMap.yaml_set_comment_before_key = \ 
     my_yaml_set_comment_before_key 

CommentedMap擴展了這種方法,你那麼可以這樣做:

yaml_str = """\ 
a: 
    b: banana 
    c: apple 
    d: orange 
    e: pear 
""" 

data = ruamel.yaml.round_trip_load(yaml_str) 
cm = data['a'] 

cm.yaml_set_comment_before_key('e', "This is Alex' comment", column=2) 
cm.yaml_set_comment_before_key('e', 'and this mine') 
ruamel.yaml.round_trip_dump(data, sys.stdout) 

獲得:

a: 
    b: banana 
    c: apple 
    d: orange 
    # This is Alex' comment 
    # and this mine one 
    e: pear 

除非您閱讀評論,否則無法查詢cm評論應該在哪一列,以便將其與e(該列在寫出數據結構時確定)對齊。您可能會試圖存儲特殊值(-1?)並嘗試在輸出期間確定此值,但流出時您的上下文很少。當然,你可以決定/設置列嵌套級(1),然後乘以縮進(你給round_trip_dump之一,默認爲2

的意見設施廿四註定保全跳閘,而不是最初修改或插入新的,因此接口不能保證穩定。考慮到這一點,請確保您創建了一個例程或一組圍繞yaml_set_comment_before_key()的例程,以進行更改,因此如果界面更改,則只有一個模塊可以更新(能夠附加評論的功能不會去走,這樣的方法可能改變但是)


¹也許不是你,而是因爲我的ruamel.yaml作者,我應該能夠找到我的未公開代碼的方式。

+1

非常感謝你@Anthon。但是,您在開始key_comment的行中留下了一條調試線,其中「'e'」應該是'key'。此外,如果您需要空白註釋行,該方法會強制您在行尾添加空白。所以我更喜歡改變它,以便用戶需要在#之後立即指定空格。 –

+0

沒有太多的調試,以及首先獲得的功能,然後將其轉換成一種方法,從而忘記了價值 - >參數轉換。感謝您的支持。我還更新了測試評論,不是空字符串(這會導致異常),但是在換行符之前沒有空格(即直接跟在'#'後面)。感謝您的支持。 – Anthon