configparser can read INI-style text and combine a default section with a selected profile.

config profile A profile stores named settings that override shared defaults.

Config Profile Overlay

profile
config_profile_overlay.py
Replay: real traced execution (multi-file project)
import configparser

text = """
[defaults]
host = localhost
port = 8000
debug = false

[dev]
debug = true
port = 8001

[prod]
host = example.com
port = 443
"""

profile = "dev"

config = configparser.ConfigParser()
config.read_string(text)

settings = dict(config["defaults"])
settings.update(config[profile])

print("profile=" + profile)
print("host=" + settings["host"])
print("port=" + settings["port"])
print("debug=" + settings["debug"])
import configparser

text = """
[defaults]
host = localhost
port = 8000
debug = false

[dev]
debug = true
port = 8001

[prod]
host = example.com
port = 443
"""

profile = "prod"

config = configparser.ConfigParser()
config.read_string(text)

settings = dict(config["defaults"])
settings.update(config[profile])

print("profile=" + profile)
print("host=" + settings["host"])
print("port=" + settings["port"])
print("debug=" + settings["debug"])
  1. text ← [defaults] host = localhost port = 8000 debug = false [dev] debug = true port = 8001 [prod] host = example.com port = 443

    3text→
    [defaults]
    host = localhost
    port = 8000
    debug = false
    
    [dev]
    debug = true
    port = 8001
    
    [prod]
    host = example.com
    port = 443
     = """4[defaults]5host = localhost6port = 80007debug = false89[dev]10debug = true11port = 80011213[prod]14host = example.com15port = 44316"""1718profile→ dev = "dev"  #@profile="prod"1920config→ ⟨ConfigParser A⟩ = configparser<module 'configparser' from '/usr/local/lib/python3.12/configparser.py'>.ConfigParser()21config⟨ConfigParser A⟩.read_string(text
    [defaults]
    host = localhost
    port = 8000
    debug = false
    
    [dev]
    debug = true
    port = 8001
    
    [prod]
    host = example.com
    port = 443
    )2223settings→ {'host': 'localhost', 'port': '8000', 'debug': 'false'} = dict(config["defaults"]<Section: defaults>)24settings→ {'host': 'localhost', 'port': '8001', 'debug': 'true'}.update(config[profile]<Section: dev>)2526print("profile=" + profiledev)27print("host=" + settings["host"]localhost)28print("port=" + settings["port"]8001)29print("debug=" + settings["debug"]true)
    outputprofile=dev
    host=localhost
    port=8001
    debug=true
  1. text ← [defaults] host = localhost port = 8000 debug = false [dev] debug = true port = 8001 [prod] host = example.com port = 443

    3text→
    [defaults]
    host = localhost
    port = 8000
    debug = false
    
    [dev]
    debug = true
    port = 8001
    
    [prod]
    host = example.com
    port = 443
     = """4[defaults]5host = localhost6port = 80007debug = false89[dev]10debug = true11port = 80011213[prod]14host = example.com15port = 44316"""1718profile→ prod = "prod"1920config→ ⟨ConfigParser A⟩ = configparser<module 'configparser' from '/usr/local/lib/python3.12/configparser.py'>.ConfigParser()21config⟨ConfigParser A⟩.read_string(text
    [defaults]
    host = localhost
    port = 8000
    debug = false
    
    [dev]
    debug = true
    port = 8001
    
    [prod]
    host = example.com
    port = 443
    )2223settings→ {'host': 'localhost', 'port': '8000', 'debug': 'false'} = dict(config["defaults"]<Section: defaults>)24settings→ {'host': 'example.com', 'port': '443', 'debug': 'false'}.update(config[profile]<Section: prod>)2526print("profile=" + profileprod)27print("host=" + settings["host"]example.com)28print("port=" + settings["port"]443)29print("debug=" + settings["debug"]false)
    outputprofile=prod
    host=example.com
    port=443
    debug=false