在Python中读取、更改和写入配置文件后保留注释

问题描述 投票:0回答:1

我是Python新手,我需要修改配置文件。我可以使用 configparser 来做到这一点,但问题是我需要将注释保留在配置文件中,而且似乎这是不可能的。我找到了更新INI文件而不删除注释,但问题并没有完全解决。

明显的解决方案是使用

ConfigObj
但评论说它已经过时了,所以我不想使用这种方法。

python configuration-files
1个回答
0
投票

如果您在使用 ConfigParser 解析之前预处理您的配置文件,并对 ConfigParser 写入的输出进行后处理,那么您可以更改 INI 文件而不删除注释。

我建议在预处理过程中将每个评论转换为一个选项(键/值对)。那么 ConfigParser 就不会抛出注释了。在后期处理过程中,您可以“解压”评论并恢复它。

为了简化过程,您可能需要子类化 ConfigParser 并重写 _read 和 write 方法。

我已经完成了此操作并在此要点中发布了 CommentConfigParser 类。它有一个限制。它不支持缩进的节标题、注释和键。它们不应该有前导空格。

class CommentConfigParser(configparser.ConfigParser):
"""Comment preserving ConfigParser.

Limitation: No support for indenting section headers,
comments and keys. They should have no leading whitespace.
"""

def __init__(self, *args, **kwargs):
    super().__init__(*args, **kwargs)
    # Backup _comment_prefixes
    self._comment_prefixes_backup = self._comment_prefixes
    # Unset _comment_prefixes so comments won't be skipped
    self._comment_prefixes = ()
    # Template to store comments as key value pair
    self._comment_template = "#{0} = {1}"
    # Regex to match the comment id prefix
    self._comment_regex = re.compile(r"^#\d+\s*=\s*")
    # List to store comments above the first section
    self._top_comments = []

def _read(self, fp, fpname):
    lines = fp.readlines()
    above_first_section = True
    # Preprocess config file to preserve comments
    for i, line in enumerate(lines):
        if line.startswith("["):
            above_first_section = False
        elif line.startswith(self._comment_prefixes_backup):
            if above_first_section:
                # Remove this line for now
                lines[i] = ""
                self._top_comments.append(line)
            else:
                # Store comment as value with unique key based on line number
                lines[i] = self._comment_template.format(i, line)

    # Feed the preprocessed file to the original _read method
    return super()._read(io.StringIO("".join(lines)), fpname)

def write(self, fp, space_around_delimiters=True):
    # Write the config to an in-memory file
    with io.StringIO() as sfile:
        super().write(sfile, space_around_delimiters)
        # Start from the beginning of sfile
        sfile.seek(0)
        lines = sfile.readlines()

    for i, line in enumerate(lines):
        # Remove the comment id prefix
        lines[i] = self._comment_regex.sub("", line, 1)

    fp.write("".join(self._top_comments + lines))
© www.soinside.com 2019 - 2024. All rights reserved.