Search code examples
pythonmergeiniconfigparser

ConfigParser changing more than expected when trying to merge sections


I had been tasked with writing a script that takes two ini files and merges them, one being the original and the second and more authoritative one being the change file. So basically anything that's new in the change file gets added, and anything that's a duplicate gets changed. And it works.... but not really how I expected.

I basically decided to do a diff on the sections using a list, and then iterate over that list and add in the deltas, but the behavior I'm seeing is hard for me to explain. Currently if there are no section changes nothing gets changed, that makes sense since you can't iterate over a list of nothing. However if even one new section gets added it merges all the option values from all over the other sections, changed or not. Which in a nut shell is what I want but I don't understand why, except for the fact that no new sections are present in the change file you'd have to add just a dummy entry to get it to actually parse.

Here's my code:

#!/usr/bin/env python
#Import Config Parser and modules, sys can be removed
#I'd like to keep it for debugging for future versions

from ConfigParser import SafeConfigParser
import sys, argparse

###############################
# Voyager Parser              #
# Written March 2014          #
# Clifford Trueman            #
###############################

__NAME__   ='voyager_parser'
__VERSION__='0.1'
__USAGE__  ='./voyager_parser -i <inputfile> -c <changefile> -o <outputfile>'
__SUMMARY__='Script to merge changes from PCE and GOI'


parse_cli = argparse.ArgumentParser(description='voyager_parser: To read in and merge GOI and PCE change ini\'s')
parse_cli.add_argument('-i', '--input', help='Input original GOI file name',required=True)
parse_cli.add_argument('-c', '--change', help='Input PCE derived ini file for changes',required=True)
parse_cli.add_argument('-o', '--output', help='Output', required=True)


#Define our global variables
#Define two parsers so we can work with multiple files
args         = parse_cli.parse_args()
parser       = SafeConfigParser()
parser2      = SafeConfigParser()
list1        = []
list2        = []
input_file1  = args.input
input_file2  = args.change
output_file  = args.output


########################
# Define our Functions #
########################

#Figuring out section diffs, Source - change files --input and --change file order matters!
def read_file_sections(input_file, compare_list):
    parser.read(input_file)
    for merge in parser.sections():
        compare_list.append(merge)
    return compare_list

#Take what we found to be different then add it
def add_delta_sections(delta_list, output_file):
    for delta in delta_list:
        parser2.add_section(delta)
        parser.write(sys.stdout)#comment in for debugging
        parser.write(open(output_file, 'w'))
    return 

######################
# Call our functions #
######################

#get a list of the sections in both ini files 
read_file_sections(input_file1, list1)
read_file_sections(input_file2, list2)

#Find out what that difference is
delta_list = list(set(list2)-set(list1))
print delta_list
#Call this to add in the changes we found
add_delta_sections(delta_list, output_file)


__name__ == '__main__'

Solution

  • So I realized after breaking down the code into sections, that ConfigParser was doing a lot of the heavy lifting I was trying to do around it, What I found was that loading the files in sequence actually did the diff I wanted internally to the module. So in a nut shell just loading two files and into a parser and then writing the parser buffer back out gives you the diff I wanted.

    anyhow here's what I changed it to:

    #Define our global variables
     27 args         = parse_cli.parse_args()
     28 parser       = SafeConfigParser()
     29 input_file1  = args.input
     30 input_file2  = args.change
     31 output_file  = args.output
     32 
     33 
     34 ########################
     35 # Define our Functions #
     36 ########################
     37 
     38 def read_file_sections(input_file):
     39     parser.read(input_file)
     40     return
     41 
     42 def merge_sections(output_file):
     43     parser.write(sys.stdout)#comment in for debugging
     44     parser.write(open(output_file, 'w'))
     45     return
     46 
     47 ######################
     48 # Call our functions #
     49 ######################
     50 
     51 read_file_sections(input_file1)
     52 read_file_sections(input_file2)
     53 merge_sections(output_file)