Обновить INI-файл без удаления комментариев
Рассмотрим следующий файл INI:
[TestSettings]
# First comment goes here
environment = test
[Browser]
# Second comment goes here
browser = chrome
chromedriver = default
...
Я использую Python 2.7 для обновления INI-файла:
config = ConfigParser.ConfigParser()
config.read(path_to_ini)
config.set('TestSettings','environment',r'some_other_value')
with open(path_to_ini, 'wb') as configfile:
config.write(configfile)
Как я могу обновить файл INI, не удаляя комментарии. Файл INI обновляется, но комментарии удаляются.
[TestSettings]
environment = some_other_value
[Browser]
browser = chrome
chromedriver = default
5 ответов
Причиной того, что комментарии в конфигурационных файлах стираются при обратной записи, является то, что метод write вообще не заботился о комментариях. Он просто записывает пары ключ / значение.
Самый простой способ обойти это - инициализировать объект configparser с настроенным префиксом комментария и allow_no_value = True. Если мы хотим оставить значения по умолчанию "#" и ";" строки комментария в файле, мы можем использовать comment_prefixes='/'.
то есть, чтобы хранить комментарии, вы должны обмануть configparser, полагая, что это не комментарий, эта строка является ключом без значения. Интересно:)
# set comment_prefixes to a string which you will not use in the config file
config = configparser.ConfigParser(comment_prefixes='/', allow_no_value=True)
config.read_file(open('example.ini'))
...
config.write(open('example.ini', 'w'))
ConfigObj сохраняет комментарии при чтении и записи INI-файлов и, кажется, делает то, что вы хотите. Пример использования для сценария, который вы описываете:
from configobj import ConfigObj
config = ConfigObj(path_to_ini)
config['TestSettings']['environment'] = 'some_other_value'
config.write()
ConfigUpdater
может обновить
.ini
файлы и сохранить комментарии: pyscaffold / configupdater .
Я не знаю, работает ли это для Python 2.
Из документов :
Ключевые отличия от ConfigParser:
- минимальные инвазивные изменения в файле конфигурации обновления,
- правильная обработка комментариев,
Если синтаксический анализатор конфигурации не изменит свою реализацию, все элементы, отсутствующие в параметрах и секциях, не будут прочитаны, поэтому, когда вы запишете их обратно, непрочитанные элементы будут потеряны. Вы можете написать свое обновление следующим образом:
def update_config(file, section, option, value, comment: str = None):
sectFound = False
lineIdx = 0
with open(file, 'r') as config:
lines = config.readlines()
lineCount = len(lines)
for line in lines:
lineIdx += 1
if sectFound and line.startswith('['): #next secion
lineIdx += -1
lines.insert(lineIdx, option + ' = ' + value)
if comment is not None:
lineIdx += 1
lines.insert(lineIdx, option + ' = ' + comment)
break
elif sectFound and line.startswith(option + ' = '):
lines.pop(lineIdx)
lines.insert(lineIdx, option + ' = ' + value)
if comment is not None:
lineIdx += 1
lines.insert(lineIdx, option + ' = ' + comment)
break
elif sectFound and lineIdx == lineCount:
lineIdx += 1
lines.insert(lineIdx, option + ' = ' + value + '\n')
if comment is not None:
lineIdx += 1
lines.insert(lineIdx, comment + '\n')
break
if line.strip() == '[' + section + ']':
sectFound = True
with open(file, 'w') as cfgfile:
cfgfile.writelines(lines)
if sectFound == False:
cfgfile.writelines('[' + section + ']\n' + option + ' = ' + value)
if comment is not None:
cfgfile.writelines(comment)
ConfigObj - лучший вариант почти во всех случаях.
Тем не менее, он не поддерживает многострочные значения без тройных кавычек, как это делает ConfigParser. В этом случае жизнеспособный вариант может быть iniparse.
Например:
[TestSettings]
# First comment goes here
multiline_option = [
first line,
second line,
]
Вы можете обновить многострочное значение таким образом.
import iniparse
import sys
c = iniparse.ConfigParser()
c.read('config.ini')
value = """[
still the first line,
still the second line,
]
"""
c.set('TestSettings', 'multiline_option', value=value)
c.write(sys.stdout)