python
41 lines · 8 steps
Layered INI config loading in Python
A small toolkit that loads INI files, merges section defaults, and extracts typed database settings.
Explained by
highlit
1from configparser import ConfigParser, ExtendedInterpolation
2from pathlib import Path
3
4
5def load_config(*paths, defaults=None):
6 parser = ConfigParser(
7 interpolation=ExtendedInterpolation(),
8 default_section="defaults",
9 )
10 if defaults:
11 parser.read_dict({"defaults": defaults})
12
13 read = parser.read([Path(p) for p in paths], encoding="utf-8")
14 if not read:
15 raise FileNotFoundError(f"no config files found among {paths!r}")
16
17 return parser
18
19
20def merged_section(parser, section, base="common"):
21 if not parser.has_section(section):
22 raise KeyError(f"missing section [{section}]")
23
24 result = {}
25 if parser.has_section(base):
26 result.update(parser[base])
27 result.update(parser[section])
28 return result
29
30
31def database_settings(parser, env):
32 section = merged_section(parser, f"db.{env}", base="db.common")
33 return {
34 "host": section.get("host", "localhost"),
35 "port": section.getint("port", fallback=5432) if hasattr(section, "getint") else int(section.get("port", 5432)),
36 "name": section["name"],
37 "user": section["user"],
38 "password": section.get("password", ""),
39 "pool_size": int(section.get("pool_size", 5)),
40 "ssl": section.get("ssl", "false").lower() in ("1", "true", "yes"),
41 }
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1ExtendedInterpolation plus a default section lets INI files reference and inherit values without repetition.
- 2Merging a base section under a specific one gives you clean layered overrides from flat config files.
- 3Config values are always strings, so coerce and default them explicitly when you read them out.
Related explainers
ruby
module AppConfig module_function def fetch(key, default: nil, required: false)
Typed environment variable config in Ruby
environment-variables
type-coercion
configuration
Intermediate
7 steps
python
from urllib.parse import urlparse class RobotsRules:
Parsing and applying a robots.txt file
parsing
longest-prefix-match
state-machine
Intermediate
10 steps
python
from pathlib import Path from django.contrib.auth.decorators import login_required from django.http import FileResponse, Http404, HttpResponseForbidden
Serving files with access checks in Django
authorization
http-caching
file-serving
Intermediate
7 steps
rust
use once_cell::sync::Lazy; use regex::Regex; static LOG_LINE: Lazy<Regex> = Lazy::new(|| {
Parsing access logs with a lazy regex in Rust
regex
lazy-initialization
parsing
Intermediate
7 steps
python
from copy import deepcopy class CowSnapshot:
Copy-on-write attribute snapshots in Python
copy-on-write
descriptors
attribute-interception
Advanced
7 steps
typescript
type Semver = { major: number; minor: number; patch: number;
Parsing and comparing semver strings in TypeScript
parsing
regular-expressions
comparison
Intermediate
9 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/layered-ini-config-loading-in-python-explained-python-f352/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.