iniconfig
iniconfig is a minimal, read-only INI-file parser maintained under the pytest-dev umbrella. It preserves section and key order, supports multi-line values, strips `#` comments from structure (not inline values in <2.3), raises `ParseError` with accurate line numbers, and rejects duplicate section names. Current stable version is 2.3.0, released 2024. Release cadence is irregular but healthy, driven by pytest ecosystem needs.
Warnings
- breaking v2.0.0 dropped Python 2 and older Python 3 (< 3.7) support and changed the packaging. The API surface stayed compatible, but pip will refuse to install 2.x on Python < 3.10 (requires_python >=3.10 as of 2.3.0).
- breaking Duplicate section names raise ParseError. Unlike stdlib configparser, iniconfig does NOT merge duplicates — the second occurrence is an error, not a silent override.
- gotcha Inline comments (e.g. `key = value # comment`) are NOT stripped by IniConfig() — the raw string including `# comment` is returned as the value. This differed silently from the README example in versions 2.0–2.2.
- gotcha ini['section']['key'] raises KeyError — not returning None — when the section or key is absent. There is no .get() short-circuit on the section-level dict.
- gotcha iniconfig is READ-ONLY. There is no write-back, set(), or save() method. Attempts to assign to ini['section']['key'] will raise TypeError or silently do nothing depending on the internal namedtuple/mapping type.
- deprecated The old py.iniconfig path (from the `py` / pylib package) was the predecessor of this standalone package. Any code doing `import py; py.iniconfig.IniConfig(...)` is using a long-deprecated shim.
- gotcha IniConfig constructor requires a path as its first positional argument even when parsing from a string via the `data=` kwarg. Passing `None` raises TypeError; pass a placeholder string like '-' or '<string>' instead.
Install
-
pip install iniconfig
Imports
- IniConfig
import iniconfig ini = iniconfig.IniConfig('example.ini') - IniConfig (from string)
import iniconfig ini = iniconfig.IniConfig(path=None, data='[s]\nk=v\n')
- IniConfig.parse (2.3+)
from iniconfig import IniConfig result = IniConfig.parse('[s]\nk=v # comment\n') - ParseError
from iniconfig import ParseError
Quickstart
import iniconfig
INI_TEXT = """
[database]
host = localhost
port = 5432
names = foo,bar
[app]
debug = true
"""
# Parse from a string (no file needed)
ini = iniconfig.IniConfig("-", data=INI_TEXT)
# Direct access — raises KeyError if key/section missing
host = ini["database"]["host"] # 'localhost'
# Safe access with default + optional converter
port = ini.get("database", "port", 5432, int) # 5432 (int)
names = ini.get("database", "names", [], lambda x: x.split(",")) # ['foo', 'bar']
missing = ini.get("database", "user", "root") # 'root'
# Membership check
assert "database" in ini
assert "ghost" not in ini
# Iterate sections
for section in ini:
print(section.name, list(section.items()))
# Catch parse errors
try:
bad = iniconfig.IniConfig("-", data="[dup]\n[dup]\n")
except iniconfig.ParseError as exc:
print(f"Parse failed: {exc}")