To jest w zasadzie możliwe, ponieważ można w obie strony, takie „start-of-file” komentuje, ale nie jest dobrze obsługiwany w bieżącym pliku ruamel.yaml 0.10, a już na pewno nie w "zaczynaniu od zera" (tzn. bez zmiany istniejącego pliku). U dołu jest łatwe i stosunkowo przyjemne rozwiązanie, ale najpierw chciałbym przedstawić brzydkie obejście i krok po kroku, jak to zrobić.
brzydki:
brzydki sposób to zrobić, to po prostu dodać komentarz do pliku przed zapisem danych YAML do niego. To jest wkładka:
f.write('# Data for Class A\n')
tuż przed ruamel.yaml.dump(...)
Krok po kroku:
wstawić komentarz do struktury danych, więc powyższe Hack nie jest to konieczne, najpierw należy upewnić się, Twoje dane d
to typ CommentedMap
.Jeśli porównać różnicę tej zmiennej d
z jednym, który ma komentarz ładując skomentował YAML powrotem do 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
Drukuje:
Comment(comment=[None, [CommentToken(value=u'# Data for Class A\n')]],
items={})
Comment(comment=None,
items={})
Comment
ma atrybut comment
który potrzebuje do ustawienia na listę 2 elementów składającą się z komentarza EOL (zawsze tylko jednego) i listy poprzednich komentarzy liniowych (w postaci CommentTokens
)
Aby utworzyć CommentToken trzeba (fake) StartMark który mówi, która kolumna zaczyna:
from ruamel.yaml.error import Mark
start_mark = Mark(None, None, None, 0, None, None) # column 0
Teraz można tworzyć token:
from ruamel.yaml.tokens import CommentToken
ct = CommentToken('# ' + comment + '\n', start_mark, None)
przypisać znak jako pierwszy element poprzedzający lista na CommentedMap:
d.ca.comment = [None, [ct]]
print d.ca # in case you want to check
daje:
Comment(comment=[None, [CommentToken(value='# Data for Class A\n')]],
items={})
I wreszcie:
print ruamel.yaml.dump(d, Dumper=ruamel.yaml.RoundTripDumper)
daje:
# 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
Oczywiście, że nie ma potrzeby tworzenia obiektu c
, że jest tylko dla ilustracji.
Co należy użyć: aby cały ćwiczenie nieco łatwiej można po prostu zapomnieć o szczegóły i poprawki w następujący sposób do CommentedBase
kiedyś:
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
a potem po prostu zrobić:
d.set_start_comment('Data for Class A')
to było łatwe :) – user3214546