기계는 거짓말하지 않는다

Python configparser config file 주석(comment) 유지 본문

Python

Python configparser config file 주석(comment) 유지

KillinTime 2025. 1. 21. 22:41

Python의 configparser 모듈로 config file을 읽고 쓰면 config file의 주석은 유지되지 않는다.

주석을 유지해야 할 때, 가능하도록 간단히 구현한 예시이다.

import configparser

def read_config_with_comments(file_path):
    """config file과 config file의 line을 함께 read"""
    with open(file_path, 'r') as file:
        lines = file.readlines()

    config = configparser.ConfigParser(allow_no_value=True)
    config.read(file_path)

    return config, lines
                    
def write_config_with_comments(config, lines, output_file, space_around_delimiters=False):
    """config file 주석 유지"""
    delim = " = " if space_around_delimiters else "="

    pending = {
        section: set(config[section].keys())
        for section in config.sections()
    }

    current_section = None

    with open(output_file, 'w') as f:
        for idx, line in enumerate(lines):
            stripped = line.strip()

            # (1) Preserve comments and blank lines as is
            if not stripped or stripped.startswith("#"):
                f.write(line)
                continue

            if stripped.startswith("[") and stripped.endswith("]"):
                # If we just finished a section, write any remaining keys
                if current_section:
                    for key in list(pending[current_section]):
                        f.write(f"{key}{delim}{config[current_section][key]}\n")
                    pending[current_section].clear()

                current_section = stripped[1:-1]
                f.write(line)
                continue

            if "=" in line and current_section:
                key = line.split("=", 1)[0].strip()
                if key in config[current_section]:
                    # Write the updated value
                    f.write(f"{key}{delim}{config[current_section][key]}\n")
                    pending[current_section].discard(key)
                else:
                    # Write the original line if the key is not updated
                    f.write(line)
            else:
                f.write(line)

            is_last = (idx == len(lines) - 1)
            next_is_section = False
            if not is_last:
                nl = lines[idx+1].strip()
                next_is_section = nl.startswith("[") and nl.endswith("]")
            if current_section and (is_last or next_is_section):
                for key in list(pending[current_section]):
                    f.write(f"{key}{delim}{config[current_section][key]}\n")
                pending[current_section].clear()

        for section, keys in pending.items():
            if keys:
                f.write(f"\n[{section}]\n")
                for key in keys:
                    f.write(f"{key}{delim}{config[section][key]}\n")
Comments