Python’s ConfigParser rejects duplicate sections and options within a single input source when strict parsing is enabled. Strict mode is already the default, but writing strict=True makes the application’s intent visible. Use it to stop an automation job when an edited INI file contains two competing definitions rather than quietly accepting whichever value happens to win.
The important boundary is one source. Strict mode does not prohibit every override across multiple files read into the same parser. A deployment that intentionally layers site settings over defaults needs a separate, documented override policy.
Understand which names collide
By default, ConfigParser normalises option names to lowercase. Inside the same section, timeout and TIMEOUT therefore collide. They are not two independent settings merely because an editor displays different capitalisation. Section names, by contrast, are case-sensitive under the normal parser behaviour.
A repeated section heading can also be rejected even when its individual option lines differ. If a person pasted a second [job] block farther down a long file, the parser should not have to guess whether that was a deliberate merge or an editing mistake.
Do not respond to a duplicate error by automatically deleting one line. Compare the intended configuration with the application’s documented settings and ask the responsible operator which definition belongs. Preserve an original copy before editing a real configuration, especially when it contains secrets or operational endpoints.
Try a small in-memory duplicate test
This example uses only standard-library modules and in-memory strings. It does not edit a configuration file, launch a job or contact a network service. It checks repeated options, case-normalised options and repeated sections, then demonstrates a deliberate two-source override.
import configparser
cases = [
('same option', '[job]\ntimeout = 10\ntimeout = 20\n',
configparser.DuplicateOptionError),
('different case', '[job]\ntimeout = 10\nTIMEOUT = 20\n',
configparser.DuplicateOptionError),
('same section', '[job]\ntimeout = 10\n[job]\nretries = 2\n',
configparser.DuplicateSectionError),
]
for label, text, expected in cases:
parser = configparser.ConfigParser(strict=True, interpolation=None)
try:
parser.read_string(text, source=label)
except expected:
print(label + ': rejected')
else:
raise AssertionError(label + ': duplicate was accepted')
parser = configparser.ConfigParser(strict=True, interpolation=None)
parser.read_string('[job]\ntimeout = 10\n', source='defaults')
parser.read_string('[job]\ntimeout = 20\n', source='site settings')
assert parser.getint('job', 'timeout') == 20
print('separate sources: timeout =', parser.getint('job', 'timeout'))
The first three lines should report rejection. The last line should report a timeout of 20 because the later source overrides the earlier one. These assertions are meant to expose the distinction: changing the example to read separate strings is not equivalent to validating one combined file.
Put validation before operational work
For a required real file, open it explicitly with the expected encoding and pass the handle to read_file(). Handle file-access failures and parsing failures before starting child processes or sending API requests. Do not assume that calling read() proves every required file was available: that method is designed to attempt a list of potential configuration locations.
If parsing fails, discard that parser instance rather than using whatever partial state it accumulated. Construct a fresh parser after the operator corrects the file. Log a concise error type and safe location information; avoid dumping the entire file or an unreviewed exception message into a broadly accessible dashboard.
interpolation=None in this example deliberately keeps values literal. It does not cause duplicate detection and should not be copied into an application that requires interpolation without checking its configuration contract. For the related distinction between a value and its presence, see checking missing dictionary keys versus explicit None values.
Keep intentional layers explicit
When your application uses defaults plus a site-specific file, document their order and which options may be overridden. Strict parsing still catches an accidental repetition within either individual file, but it is not an audit trail of where every final value originated.
If your requirements forbid the same setting in any two files, validate each file separately and compare the relevant section-option pairs before merging. Decide how inherited DEFAULT settings fit that policy. Do not claim that strict=True alone implements this stronger rule, and do not disable strict mode globally to permit a single legitimate deployment override.
Final review checklist
- Does each required input file open successfully?
- Are duplicates rejected within each source, including normalised option names?
- Are section names and any custom option-name transformations documented?
- Is the order of intentional source overrides explicit?
- Are required sections, types and allowed values checked after parsing?
- Does a failure stop the job before it performs operational work?
A syntactically valid INI file can still contain an invalid timeout, an unknown setting or a dangerous endpoint. Duplicate detection is one preflight check, not complete schema validation or a trust boundary for arbitrary large inputs.
Source: Python’s ConfigParser documentation, especially strict parser behaviour, option-name transformation and duplicate exceptions.
This tutorial is for educational purposes. Test carefully before using code in production. The site owner is not responsible for misuse, damage, data loss or production issues.



