Python TOML Configuration — Core Concepts
TOML (Tom’s Obvious Minimal Language) has become Python’s preferred configuration format since the adoption of pyproject.toml as the standard project configuration file. Python 3.11 added tomllib to the standard library, cementing TOML’s role in the Python ecosystem.
Reading TOML
Python 3.11+ (Built-in)
import tomllib
with open("config.toml", "rb") as f: # Note: binary mode required
config = tomllib.load(f)
# From a string
config = tomllib.loads("""
[database]
host = "localhost"
port = 5432
""")
tomllib opens files in binary mode because TOML files must be UTF-8 — the module handles decoding internally.
Python 3.10 and Earlier
pip install tomli
import tomli
with open("config.toml", "rb") as f:
config = tomli.load(f)
tomli is the package that became tomllib in the standard library. The API is identical.
TOML Type Mapping
| TOML | Python | Example |
|---|---|---|
| String | str | name = "Alice" |
| Integer | int | port = 8080 |
| Float | float | rate = 3.14 |
| Boolean | bool | debug = true |
| Date/Time | datetime | created = 2026-03-28T10:00:00Z |
| Date | date | birthday = 2000-01-15 |
| Time | time | alarm = 07:30:00 |
| Array | list | tags = ["python", "toml"] |
| Table | dict | [section] |
Unlike YAML, TOML has no implicit type coercion. "true" is always a string, true is always a boolean. This eliminates an entire class of bugs.
TOML Structure
Tables (Sections)
[database]
host = "localhost"
port = 5432
[database.credentials]
user = "admin"
password = "secret"
Becomes:
{
"database": {
"host": "localhost",
"port": 5432,
"credentials": {
"user": "admin",
"password": "secret",
}
}
}
Arrays of Tables
[[servers]]
name = "alpha"
ip = "10.0.0.1"
[[servers]]
name = "beta"
ip = "10.0.0.2"
Becomes:
{"servers": [{"name": "alpha", "ip": "10.0.0.1"}, {"name": "beta", "ip": "10.0.0.2"}]}
Inline Tables
point = { x = 1, y = 2 }
Use inline tables for small, simple groupings. Use full tables for anything more than two or three keys.
Writing TOML
tomli-w (Minimal Writer)
pip install tomli-w
import tomli_w
config = {
"database": {
"host": "localhost",
"port": 5432,
},
"features": ["auth", "logging"],
}
with open("config.toml", "wb") as f: # Binary mode
tomli_w.dump(config, f)
# To string
toml_string = tomli_w.dumps(config)
tomlkit (Round-Trip Preserving)
tomlkit preserves comments, formatting, and ordering:
pip install tomlkit
import tomlkit
with open("pyproject.toml") as f:
doc = tomlkit.parse(f.read())
# Modify while preserving formatting
doc["project"]["version"] = "2.0.0"
with open("pyproject.toml", "w") as f:
f.write(tomlkit.dumps(doc))
This is essential for tools that edit pyproject.toml programmatically.
pyproject.toml: Python’s Configuration Hub
The most important TOML file in Python is pyproject.toml:
[project]
name = "mypackage"
version = "1.0.0"
description = "My awesome package"
requires-python = ">=3.11"
dependencies = [
"httpx>=0.24",
"pydantic>=2.0",
]
[project.optional-dependencies]
dev = ["pytest", "ruff", "mypy"]
[tool.ruff]
line-length = 88
[tool.pytest.ini_options]
testpaths = ["tests"]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
This single file replaces setup.py, setup.cfg, requirements.txt, and tool-specific config files.
TOML vs. Other Config Formats
| Feature | TOML | JSON | YAML | INI |
|---|---|---|---|---|
| Comments | Yes | No | Yes | Yes |
| Typed values | Yes | Partial | Yes (with gotchas) | No |
| Nested structure | Yes | Yes | Yes | Limited |
| Arrays | Yes | Yes | Yes | No |
| Stdlib support | 3.11+ | Yes | No | Yes |
| Surprising behavior | Minimal | None | Frequent | Some |
| Multiline strings | Yes | No | Yes | No |
Common Misconception
“TOML is just a better INI format.” While TOML looks similar to INI files, it’s a fully specified format with proper types, nested tables, arrays, datetime support, and a formal grammar. INI files have no standard — every parser handles them differently. TOML has exactly one correct way to parse any valid document.
One Thing to Remember
TOML gives you typed configuration with no surprises — use tomllib (3.11+) for reading, tomlkit for editing existing files with preserved formatting, and put your project config in pyproject.toml.
See Also
- Python Csv Processing Learn how Python reads and writes spreadsheet-style CSV files — the universal language of data tables.
- Python Json Handling See how Python talks to the rest of the internet using JSON — the universal language apps use to share information.
- Python Template Strings See how Python's Template strings let you fill in blanks safely, like a Mad Libs game that can't go wrong.
- Ci Cd Why big apps can ship updates every day without turning your phone into a glitchy mess — CI/CD is the behind-the-scenes quality gate and delivery truck.
- Containerization Why does software that works on your computer break on everyone else's? Containers fix that — and they're why Netflix can deploy 100 updates a day without the site going down.