Обновить 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)
Другие вопросы по тегам