A Python automation job can read an INI file successfully and then fail when retrieving a value containing a percent sign. The usual cause is ConfigParser’s default basic interpolation, which interprets percent-based substitution syntax. For a configuration format intended to contain literal strings, construct the parser with interpolation=None. Do not blindly replace percent signs throughout an existing configuration.
This distinction is useful for URL templates, progress labels and other settings that legitimately contain percent characters. A percent-encoded URL is not necessarily corrupt simply because an interpolation-enabled parser rejects it. First establish whether the application’s configuration contract promises literal values or references to other settings.
Recognise a retrieval error rather than a missing file
ConfigParser performs interpolation on demand. Reading a string into the parser and retrieving an option are different steps. A successful read therefore does not prove that every option can be expanded. If the failure appears at get() or section lookup, inspect the exception and the option’s intended syntax before blaming file access.
Do not print a full configuration into an unattended job log to diagnose one bad value. Settings can include credentials, private endpoints or personal data. Record the option name and exception type while keeping the actual sensitive value out of the report.
Reproduce the difference without touching a real job
The following self-contained example uses an in-memory INI document. It demonstrates the default retrieval failure and then checks literal parsing. It neither contacts the example address nor writes any files. Run it with Python 3 in a disposable development session.
import configparser
text = """[job]
url = https://example.invalid/export?label=night%20run
progress = 80%
reference = %(root)s/output
root = /srv/example
"""
expanded = configparser.ConfigParser()
expanded.read_string(text)
try:
expanded.get("job", "url")
except configparser.InterpolationSyntaxError:
print("Default interpolation rejected a literal percent value")
else:
raise AssertionError("Expected an interpolation error")
literal = configparser.ConfigParser(interpolation=None)
literal.read_string(text)
assert literal.get("job", "url") == (
"https://example.invalid/export?label=night%20run"
)
assert literal.get("job", "progress") == "80%"
assert literal.get("job", "reference") == "%(root)s/output"
assert expanded.get("job", "reference") == "/srv/example/output"
assert expanded.get("job", "progress", raw=True) == "80%"
print("Literal, expanded and per-read raw values checked")
The unchanged reference string is an important assertion. Turning interpolation off fixes literal percent retrieval, but also stops intentional references from expanding. If a downstream command expects an expanded path, changing parser construction alone could hand it an unresolved placeholder instead.
Choose one policy deliberately
Literal application settings: use ConfigParser(interpolation=None) consistently where the application loads these settings. Preserve the percent characters as data. This does not decode URLs, expand environment variables or substitute shell expressions.
Existing basic interpolation: retain the default parser when reference expansion is part of the documented format. The Python documentation specifies a doubled percent sign for a literal percent in that format: an INI value written as 80%% is retrieved as 80%. Update configuration and tests together rather than introducing an unexplained mixed convention.
A single diagnostic retrieval: get(section, option, raw=True) skips interpolation for that read. It does not permanently change the parser; other reads can still expand references or fail. Avoid treating a raw diagnostic result as proof that the normal production lookup will work.
Validate the resulting settings before work begins
After deciding on the parsing policy, validate required sections and options and check their actual semantic values. A successfully returned string is not proof that a URL is approved, a directory exists or an account has permission. Likewise, disabling interpolation is not a secret-storage mechanism or a general security sandbox.
For unattended jobs, keep this validation ahead of external commands and file updates. The related tutorial on checking command dependencies before Python automation starts covers another independent preflight: an executable lookup does not replace configuration validation.
Migration checklist and questions
- Back up the existing settings and identify consumers that share the file.
- Test a literal percent, a percent-encoded URL and a deliberate reference.
- Verify the strings passed to downstream code without exposing secrets.
- Keep the previous parser policy available for rollback if intentional expansion was missed.
Should I switch to ExtendedInterpolation? Not merely to hide the current error. It defines a different reference syntax and needs its own explicit configuration contract.
Does a raw read preserve the original file exactly? No. Retrieving an uninterpolated value is not a byte-for-byte file preservation guarantee; do not rewrite a live settings file merely to inspect it.
Source: Python’s configparser documentation, particularly interpolation, literal-percent escaping and parser customisation.
This tutorial is for educational purposes. Test carefully before production use. The site owner is not responsible for misuse, damage, data loss or production issues.



