Desk Clarity

Everyday document & browser help

ConfigParser Lowercases Keys: Set the Name Transformation Before Reading

By ON Lab · Sources checked November 9, 2026

Assign parser.optionxform = str before parsing when case must be preserved. Once names have been normalized, changing the setting cannot reconstruct their original spelling.

Reproduce the boundary in a disposable example

Mixed-case INI option names are lowercased when written back.

import configparser, io
text = "[service]\nApiToken = placeholder\n"
normal = configparser.ConfigParser()
normal.read_string(text)
assert list(normal["service"]) == ["apitoken"]
normal.optionxform = str
assert list(normal["service"]) == ["apitoken"]
exact = configparser.ConfigParser()
exact.optionxform = str
exact.read_string(text)
assert list(exact["service"]) == ["ApiToken"]
output = io.StringIO()
exact.write(output)
assert "ApiToken = placeholder" in output.getvalue()
print("name spelling preserved before parsing")

Read the result before changing application code

Assign the function itself; calling optionxform(str()) only transforms an empty name and does not replace the transformation. The middle assertion catches a tempting partial fix: the parser already stored the lowercased name. Reload the original content into a newly configured parser and test the saved output. Do not overwrite the only original INI file during diagnosis.

Choose the behavior your caller needs

Preserving case also changes lookup expectations. Update callers to use the intended spelling consistently, and test files with two keys differing only by case before rollout. If the application promises case-insensitive names, lowercasing may be correct and only the output appearance is surprising. A parser round trip can also change comments or spacing, so use a backup and review the diff even when names are right.

Scope and limits

This is about option-name transformation, not value case, section-name rewriting or a promise of formatting-preserving INI editing.

Related: compare the adjacent data or execution boundary.

The embedded example was executed in an isolated local Python process on November 9, 2026. It is a reproducible boundary check, not a test of your full application.

Sources