Skip to content

Instantly share code, notes, and snippets.

@Jckf
Created May 21, 2020 00:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Jckf/175236b8b68bd9a6684130ae5ec0c28d to your computer and use it in GitHub Desktop.
Save Jckf/175236b8b68bd9a6684130ae5ec0c28d to your computer and use it in GitHub Desktop.
Reading and writing INI files in Python (for Luke Saward)
# Crude ini file reader/writer. Tries to retain original file formatting when writing.
# Author: Jim C K Flaten <jckf@jckf.no>
import os
import json
def parse_ini(filename, updates = {}):
# Prepare some variables we need.
ini = {}
section = None
# Open this file(s).
r = open(filename, "r")
w = open(filename + ".tmp", "w") if updates else None
# Loop over each line.
for raw_line in r.readlines():
# Remove leading and trailing whitespace/special characters.
line = raw_line.strip();
# Empty line or comment.
if not line or line[-1] == "#":
if w: w.write(raw_line)
continue
# Section header.
if line[:1] == "[" and line[-1] == "]":
# We have left-over updates for the current section (new keys). Write them before entering a new section.
if section and section in updates:
for key in updates[section]:
w.write(key + "=" + str(updates[section][key]) + "\n")
# End the section with an empty line for readability.
w.write("\n")
del updates[section]
# Section name without the brackets.
section = line[1:-1]
# Initialize an empty dictionary to store this section in.
if not section in ini.keys():
ini[section] = {}
# Copy the line "as is" if we're writing.
if w: w.write(raw_line)
continue
# We've found something that's not inside a section? Skip it.
if not section:
# Copy the line "as is" if we're writing.
if w: w.write(raw_line)
continue
# Split into key and value pair.
pair = line.split("=")
# Clean up the values in case there was whitespace around the equals character.
key = pair[0].rstrip()
value = pair[1].lstrip()
# Do we have an update for this?
if section in updates.keys() and key in updates[section].keys():
# Pop value from the updates dict to signify that we've completed it.
value = updates[section].pop(key)
# If we're writing and the value isn't None (to be deleted), write the new value.
if w and value != None: w.write(key + "=" + str(value) + "\n")
# No more updates left in this section.
if not updates[section]:
del updates[section]
elif w:
# No update, but we're writing. Copy the line "as is".
w.write(raw_line)
# Store it for return, unless it was unset.
if value != None:
ini[section][key] = value
# Close the read handle.
r.close()
# Close the write handle if we have one, and replace the file with our new one.
if w:
w.close()
os.replace(filename + ".tmp", filename)
return ini
#####################
# EXAMPLE USE BELOW #
#####################
# Load the current ini file.
config = parse_ini("config.ini")
# Fetch some values from the dict to look at.
authors = set(map(str.strip, config["General"]["authors"].split(",")))
rerecord_count = int(config["General"]["rerecord_count"])
flipflop = "flipflop" in config["General"];
print("Current authors are: " + ", ".join(authors))
print("Current rerecord count is: " + str(rerecord_count))
print("Current flip-flop state: " + str(flipflop))
print()
# Modify some of the data.
authors.add("Jim") # "Jim" will always be added to authors list, but never duplicated.
rerecord_count += 1 # Rerecord count will increment by 1 every execution.
flipflop = None if flipflop else True # Flip-flop will switch back and forth between not existing, and being True.
print("New authors are: " + ", ".join(authors))
print("New rerecord count is: " + str(rerecord_count))
print("New flip-flop state: " + str(flipflop))
# Create an update dict.
updates = {}
updates["General"] = {}
updates["General"]["authors"] = ", ".join(authors)
updates["General"]["rerecord_count"] = rerecord_count
updates["General"]["flipflop"] = flipflop
# Write the updates to file.
parse_ini("config.ini", updates)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment