2015-06-23 2 views
6

Я пишу файл YAML, используя https://pypi.python.org/pypi/ruamel.yamlКак я могу добавить комментарий к файлу YAML в Python

Этот код выглядит так:

import ruamel.yaml 
from ruamel.yaml.comments import CommentedSeq 

d = {} 
for m in ['B1', 'B2', 'B3']: 
    d2 = {} 
    for f in ['A1', 'A2', 'A3']: 
     d2[f] = CommentedSeq(['test', 'test2']) 
     if f != 'A2': 
      d2[f].fa.set_flow_style() 
    d[m] = d2 

    with open('test.yml', "w") as f: 
     ruamel.yaml.dump(
      d, f, Dumper=ruamel.yaml.RoundTripDumper, 
      default_flow_style=False, width=50, indent=8) 

Я просто хочу, чтобы добавить комментарий в верхней части, как :

# Data for Class A 

Перед данными YAML.

ответ

3

В блоке with вы можете записать все, что хотите, в файл. Так как вы только должны комментарий в верхней части, добавьте вызов f.write() перед вызовом ruamel:

with open('test.yml', "w") as f: 
    f.write('# Data for Class A\n') 
    ruamel.yaml.dump(
     d, f, Dumper=ruamel.yaml.RoundTripDumper, 
     default_flow_style=False, width=50, indent=8) 
+0

Это было легко :) – user3214546

1

Это в принципе возможно, потому что вы можете туда-обратно, например «запуск из файла» комментарии, но он не очень хорошо поддерживается в текущем файле ruamel.yaml 0.10 и, конечно, не «начинать с нуля» (т. е. не изменять существующий файл). В нижней части это легкое относительно хорошее решение, но сначала я хотел бы представить уродливое обходное решение и поэтапно, как это сделать.

Гадкий:
Уродливый способ сделать это просто добавить свой комментарий к файлу, прежде чем записать данные YAML к нему. То есть вставка:

f.write('# Data for Class A\n') 

непосредственно перед ruamel.yaml.dump(...)

Шаг за шагом:
Чтобы вставить комментарий о структуре данных, поэтому приведенные выше хак не является необходимым, вы первый необходимо, чтобы убедиться, ваши данные d - это тип CommentedMap.Если вы сравните разницу этой d переменной с одним, который имеет комментарий, загрузив комментируемой YAML обратно в c

import ruamel.yaml 
from ruamel.yaml.comments import Comment, CommentedSeq, CommentedMap 

d = CommentedMap()    # <<<<< most important 
for m in ['B1', 'B2', 'B3']: 
    d2 = {} 
    for f in ['A1', 'A2', 'A3']: 
     d2[f] = CommentedSeq(['test', 'test2']) 
     if f != 'A2': 
      d2[f].fa.set_flow_style() 
    d[m] = d2 

yaml_str = ruamel.yaml.dump(d, Dumper=ruamel.yaml.RoundTripDumper, 
          default_flow_style=False, width=50, indent=8) 

assert not hasattr(d, Comment.attrib) # no attribute on the CommentedMap 

comment = 'Data for Class A' 
commented_yaml_str = '# ' + comment + '\n' + yaml_str 
c = ruamel.yaml.load(commented_yaml_str, Loader=ruamel.yaml.RoundTripLoader) 
assert hasattr(c, Comment.attrib) # c has the attribute 
print c.ca       # and this is what it looks like 
print d.ca       # accessing comment attribute creates it empty 
assert hasattr(d, Comment.attrib) # now the CommentedMap has the attribute 

Печатается:

Comment(comment=[None, [CommentToken(value=u'# Data for Class A\n')]], 
    items={}) 
Comment(comment=None, 
    items={}) 

Comment имеет атрибут comment, который необходимо должен быть установлен в список из 2 элементов, который состоит из комментария EOL (всегда только один) и списка предыдущих комментариев строки (в форме CommentTokens)

Для создания CommentToken вам нужен (поддельный) StartMark, который говорит, какой столбец начинается:

from ruamel.yaml.error import Mark 
start_mark = Mark(None, None, None, 0, None, None) # column 0 

Теперь вы можете создать маркер:

from ruamel.yaml.tokens import CommentToken 

ct = CommentToken('# ' + comment + '\n', start_mark, None) 

Назначьте маркер в качестве первого элемента предыдущего список на вашем CommentedMap:

d.ca.comment = [None, [ct]] 
print d.ca # in case you want to check 

дает:

Comment(comment=[None, [CommentToken(value='# Data for Class A\n')]], 
    items={}) 

И наконец:

print ruamel.yaml.dump(d, Dumper=ruamel.yaml.RoundTripDumper) 

дает:

# Data for Class A 
B1: 
     A1: [test, test2] 
     A3: [test, test2] 
     A2: 
     - test 
     - test2 
B2: 
     A1: [test, test2] 
     A3: [test, test2] 
     A2: 
     - test 
     - test2 
B3: 
     A1: [test, test2] 
     A3: [test, test2] 
     A2: 
     - test 
     - test2 

Конечно, вам не нужно, чтобы создать c объект, то есть только для иллюстрации.

Что вы должны использовать: Для того, чтобы сделать все упражнение несколько легче, вы можете просто забыть о деталях и пластыря в следующем методе CommentedBase раз:

from ruamel.yaml.comments import CommentedBase 

def set_start_comment(self, comment, indent=0): 
    """overwrites any preceding comment lines on an object 
    expects comment to be without `#` and possible have mutlple lines 
    """ 
    from ruamel.yaml.error import Mark 
    from ruamel.yaml.tokens import CommentToken 
    if self.ca.comment is None: 
     pre_comments = [] 
     self.ca.comment = [None, pre_comments] 
    else: 
     pre_comments = self.ca.comments[1] 
    if comment[-1] == '\n': 
     comment = comment[:-1] # strip final newline if there 
    start_mark = Mark(None, None, None, indent, None, None) 
    for com in comment.split('\n'): 
     pre_comments.append(CommentToken('# ' + com + '\n', start_mark, None)) 

if not hasattr(CommentedBase, 'set_start_comment'): # in case it is there 
    CommentedBase.set_start_comment = set_start_comment 

, а затем просто сделать:

d.set_start_comment('Data for Class A') 
+0

Это похоже на большую работу, только для обхода вызова 'f.write()'. В чем польза? Тем не менее, похоже, что это будет иметь смысл как встроенная часть румаля, возможно, отправить им запрос на тяну? – dimo414

+2

@ dimo414 Да, если это только для верхней части файла, это накладные расходы. Но если вы добавите его к данным, а затем используйте данные в списке и напишите, что это работает. Я хотел бы это сделать, чтобы включить в ruamel.yaml в общем виде, для этого не нужен PR, поскольку я автор. Таким образом, это будет в следующем обновлении, которое я загружу в PyPI (10.1). Тогда я могу уменьшить ответ только на последнюю строку ;-) – Anthon

Смежные вопросы