Se hai aperto un progetto Rust, hai già incontrato TOML. Se hai toccato un pacchetto Python che usa
pyproject.toml, l'hai usato anche tu. TOML — Tom's Obvious Minimal Language —
è il formato di file di configurazione che continua silenziosamente a vincere tra gli sviluppatori stanchi
delle mine whitespace di YAML e del rifiuto di JSON di consentire commenti. Creato da Tom Preston-Werner,
cofondatore di GitHub, TOML è stato progettato attorno a un'idea: un formato di configurazione dovrebbe essere
così ovvio che puoi leggerlo senza una specifica. Vediamo se mantiene quella promessa.
Cos'è Esattamente TOML?
TOML è un formato di file di configurazione con tre obiettivi di design espliciti, enunciati subito all'inizio della specifica ufficiale: dovrebbe essere ovvio da leggere, minimale in complessità, e mappare inequivocabilmente su una hash table (un dizionario/mappa nella maggior parte dei linguaggi). Quel terzo obiettivo è quello chiave — ogni file TOML valido ha esattamente un risultato di parsing corretto. Nessuna coercizione di tipo sorprendente, nessuna mine booleane stile YAML, nessuna ambiguità su se un valore è una stringa o un numero.
Il formato prende in prestito lo stile degli header di sezione dai vecchi file INI ma aggiunge tipi propri, array e tabelle annidate. Il risultato è qualcosa che sembra familiare a chiunque abbia modificato un file di configurazione prima, ma con abbastanza struttura che un parser può darti un vero modello dati tipizzato. La versione 1.0.0 della specifica è stata rilasciata nel gennaio 2021 dopo anni di raffinamento — puoi consultare la specifica TOML su GitHub se vuoi approfondire i casi limite.
Le Basi: Coppie Chiave-Valore e Commenti
Un file TOML è composto da coppie chiave-valore. Chiavi e valori sono separati da =,
e i commenti iniziano con #. Semplice.
# Cargo.toml — Rust package manifest
[package]
name = "image-resizer"
version = "0.4.2"
edition = "2021"
authors = ["Ada Lovelace <[email protected]>"]
description = "Fast image resizing with Lanczos3 resampling"
license = "MIT"
repository = "https://github.com/example/image-resizer"
# Integers, floats, booleans — all native types
max_threads = 8
quality_default = 0.85
verbose_logging = falseNessuna virgoletta necessaria sulla maggior parte delle chiavi. I valori sono tipizzati —
8 è un intero, 0.85 è un float, false è un booleano. Nessuna
supposizione, nessuna coercizione implicita basata sull'aspetto del valore. Questo è il TOML quotidiano
che scriverai il 90% delle volte.
Tipi di Stringa: Basic, Literal e Multiline
TOML ha quattro tipi di stringa. Questo copre ogni caso reale in modo pulito:
# Basic strings — double quotes, support escape sequences
greeting = "Hello, \nworld!"
path = "C:\\Users\\ada\\Documents"
# Literal strings — single quotes, no escape processing at all
regex_pattern = '\d{4}-\d{2}-\d{2}'
windows_path = 'C:\Users\ada'
# Multiline basic string — triple double quotes
sql_query = """
SELECT user_id, email, created_at
FROM users
WHERE active = true
AND created_at > '2024-01-01'
ORDER BY created_at DESC
"""
# Multiline literal string — triple single quotes, no escapes
shell_script = '''
#!/bin/bash
echo "Deploying $APP_NAME to $ENV"
kubectl apply -f k8s/
'''Il tipo di stringa literal ('virgolette singole') è quello che le persone dimenticano
esista, ed è genuinamente utile — i pattern regex e i percorsi Windows sono molto più puliti senza il
raddoppio degli escape. Scegli lo stile di virgolette che ti fa scrivere meno backslash.
Numeri, Booleani e Datetime
I tipi nativi di TOML coprono tutto ciò che metteresti effettivamente in un file di configurazione. In particolare, ha supporto datetime di prima classe — qualcosa che YAML tecnicamente ha ma gestisce in modo incoerente tra i parser.
# Integers — underscores allowed as separators (like numeric literals in code)
max_connections = 1_000_000
port = 5432
hex_color = 0xFF6B6B # hex prefix supported
octal_permissions = 0o755 # octal prefix supported
# Floats
pi = 3.14159265
compression_ratio = 1.5e-3
infinity_val = inf # special values: inf, -inf, nan
# Booleans — lowercase only (not True, TRUE, yes, on)
ssl_enabled = true
dry_run = false
# Datetimes — RFC 3339 format
created_at = 2024-03-15T09:30:00Z
updated_at = 2024-03-15T14:22:10+05:30
log_date = 2024-03-15 # local date (no time)
backup_time = 03:00:00 # local time (no date)true e false —
non True, TRUE, yes, on, o nessuna delle altre varianti
che YAML 1.1 accetta. Questo è intenzionale. Se hai bisogno di una stringa "true", mettila tra virgolette.Tabelle: Gli Header di Sezione Stile INI
Le tabelle in TOML sono definite con la sintassi [header]. Tutto ciò che segue
un header appartiene a quella tabella finché non appare il prossimo header. Questa è la caratteristica
che rende TOML familiare — è essenzialmente file INI ma con tipi.
[database]
host = "db.internal"
port = 5432
name = "app_production"
pool_size = 20
[database.credentials]
username = "app_user"
# Don't put real passwords here — use env vars or a secrets manager
password_env = "DB_PASSWORD"
[server]
host = "0.0.0.0"
port = 8080
workers = 4
[server.tls]
enabled = true
cert_file = "/etc/ssl/certs/app.crt"
key_file = "/etc/ssl/private/app.key"Gli header puntati come [database.credentials] creano tabelle annidate. Il risultato
analizzato è esattamente quello che ti aspetteresti: un oggetto database con un oggetto
credentials annidato. Puoi anche scrivere tabelle inline per casi semplici — ne parleremo sotto.
Array e Array di Tabelle
Gli array in TOML usano parentesi quadre e possono estendersi su più righe. La caratteristica
davvero distintiva di TOML è l'Array di Tabelle — definito con doppie parentesi
[[header]]. Questa è la risposta di TOML a "come esprimo una lista di oggetti?" senza
che sembri JSON.
# Regular arrays — can be split across lines, trailing comma is fine
allowed_origins = [
"https://app.example.com",
"https://admin.example.com",
"http://localhost:3000",
]
supported_formats = ["jpeg", "png", "webp", "avif"]
retry_delays_ms = [100, 250, 500, 1000, 2000]
# Array of Tables — [[double brackets]]
# Each [[servers]] header appends a new object to the servers array
[[servers]]
name = "web-01"
ip = "10.0.1.10"
role = "primary"
tags = ["web", "prod"]
[[servers]]
name = "web-02"
ip = "10.0.1.11"
role = "replica"
tags = ["web", "prod"]
[[servers]]
name = "db-01"
ip = "10.0.2.10"
role = "primary"
tags = ["database", "prod"]La sintassi [[servers]] viene analizzata in un array di tre oggetti — equivalente
a "servers": [{...}, {...}, {...}] in JSON. È verboso rispetto agli array JSON di oggetti,
ma il vantaggio è la leggibilità quando ogni elemento ha molti campi. Puoi vedere questo pattern
abbondantemente nei
manifest Cargo.toml
per la definizione di più target binari, esempi e voci bench.
Tabelle Inline: Compatte su Una Riga
Quando una tabella ha solo un paio di campi e non vuoi un intero header di sezione per essa, le tabelle inline ti permettono di scriverla su una singola riga:
[build]
# Inline table — must stay on one line
target = { arch = "x86_64", os = "linux", libc = "musl" }
# Equivalent to writing:
# [build.target]
# arch = "x86_64"
# os = "linux"
# libc = "musl"
[feature_flags]
auth = { enabled = true, rollout_pct = 100 }
search = { enabled = true, rollout_pct = 50 }
beta = { enabled = false, rollout_pct = 0 }Le tabelle inline devono restare su una singola riga e non possono essere estese in seguito con
un [header]. Usale per piccoli gruppi di valori coesi — sono ottime per cose come coppie
di coordinate, target di build, o semplici configurazioni di flag. Non usarle quando hai più di tre o
quattro campi; a quel punto una tabella normale è più leggibile.
TOML nel Mondo Reale
TOML ha conquistato una nicchia forte negli ecosistemi Rust e Python, e sta guadagnando terreno altrove. Ecco dove lo incontrerai quotidianamente:
- Rust — Cargo.toml: ogni progetto Rust ne ha uno. Definisce i metadati del pacchetto, le dipendenze, le funzionalità e i target di build. Il riferimento al manifest Cargo è la guida all'utilizzo reale di TOML più dettagliata che troverai.
- Python — pyproject.toml: PEP 518 e PEP 621 hanno standardizzato TOML come formato dei metadati del progetto Python. Poetry, Hatch, PDM e setuptools lo leggono tutti.
- Deno: il file di configurazione Deno supporta TOML oltre a JSON.
- Hugo: il generatore di siti statici Hugo accetta TOML come formato di configurazione e front-matter — lo vedrai tra i delimitatori
+++in cima ai file Markdown. - uv: il veloce gestore di pacchetti Python di Astral usa pyproject.toml per tutta la configurazione, rendendo TOML lo standard de facto per i nuovi strumenti Python.
Se devi convertire una configurazione esistente tra formati, i convertitori TOML to JSON e JSON to TOML gestiscono la mappatura strutturale. Oppure usa il TOML Formatter per pulire la spaziatura incoerente in un file esistente, e il TOML Validator per individuare gli errori di sintassi prima che emergano in produzione.
Parsing TOML in Python
A partire da Python 3.11, la libreria standard include
tomllib
— nessuna dipendenza esterna necessaria. Per Python 3.9 e 3.10, il backport
tomli
ha un'API identica, quindi puoi passare tra di loro con un singolo alias di importazione.
import sys
if sys.version_info >= (3, 11):
import tomllib
else:
import tomli as tomllib # pip install tomli
# tomllib only reads binary mode — open with "rb"
with open("pyproject.toml", "rb") as f:
config = tomllib.load(f)
# Types match TOML exactly: str, int, float, bool, datetime, list, dict
project_name = config["project"]["name"] # str
python_requires = config["project"]["requires-python"] # str
dependencies = config["project"]["dependencies"] # list[str]
print(f"Project: {project_name}")
print(f"Requires Python: {python_requires}")
print(f"Dependencies ({len(dependencies)}):")
for dep in dependencies:
print(f" {dep}")
# Parse from a string with tomllib.loads()
raw = """
[server]
host = "localhost"
port = 8080
debug = true
"""
server_config = tomllib.loads(raw)
print(server_config["server"]["port"]) # 8080 (int, not "8080")Nota che tomllib.load() richiede la modalità binaria ("rb"). Questo è
intenzionale — TOML richiede la codifica UTF-8, e aprire in modalità binaria permette al parser di gestire
il controllo della codifica stesso. È una piccola insidia che sorprende le persone la prima volta.
Parsing TOML in Rust
In Rust, il crate toml
è la scelta standard. Si integra strettamente con serde, quindi puoi deserializzare
direttamente nelle tue struct con un minimo di codice boilerplate:
use serde::Deserialize;
use std::fs;
#[derive(Debug, Deserialize)]
struct AppConfig {
server: ServerConfig,
database: DatabaseConfig,
feature_flags: FeatureFlags,
}
#[derive(Debug, Deserialize)]
struct ServerConfig {
host: String,
port: u16,
workers: usize,
}
#[derive(Debug, Deserialize)]
struct DatabaseConfig {
host: String,
port: u16,
name: String,
pool_size: u32,
}
#[derive(Debug, Deserialize)]
struct FeatureFlags {
enable_beta: bool,
max_upload_mb: u32,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let raw = fs::read_to_string("config.toml")?;
let config: AppConfig = toml::from_str(&raw)?;
println!("Server: {}:{}", config.server.host, config.server.port);
println!("DB pool size: {}", config.database.pool_size);
println!("Beta enabled: {}", config.feature_flags.enable_beta);
Ok(())
}Aggiungi toml = "0.8" e serde = { version = "1", features = ["derive"] }
nelle tue [dependencies] in Cargo.toml e sei pronto. Le macro derive di serde gestiscono tutta
la mappatura dei campi. Se i nomi dei campi della tua struct usano snake_case ma le chiavi TOML usano
kebab-case, aggiungi #[serde(rename_all = "kebab-case")] a livello di struct e tutto
si mappa automaticamente.
TOML vs YAML vs JSON — Quando Scegliere Quale
Questa domanda viene posta su ogni nuovo progetto. Ecco il resoconto onesto:
- TOML: migliore per file di configurazione che gli esseri umani scrivono e mantengono, dove i valori tipizzati sono importanti e la nidificazione è superficiale-moderata. Punto di forza: configurazioni di app, manifest di build, impostazioni degli strumenti. Si rompe per la nidificazione profonda — 4+ livelli diventano scomodi con header di sezione ripetuti.
- YAML: migliore quando scrivi dati strutturati con molte liste-di-oggetti (manifest Kubernetes, workflow GitHub Actions). Il supporto per stringhe multiriga è genuinamente migliore di TOML. Lo svantaggio: la sensibilità agli spazi bianchi e la coercizione dei tipi YAML 1.1 creano bug reali in pratica.
- JSON: migliore per lo scambio di dati macchina-macchina, API, e quando hai bisogno del supporto toolchain più ampio possibile. Non ottimo per configurazioni mantenute dall'uomo — nessun commento, e l'escaping delle stringhe è tedioso.
Un pyproject.toml Reale
Per mettere tutto insieme, ecco un realistico pyproject.toml per una libreria Python —
il tipo che troveresti in un moderno progetto open-source. Nota come il formato porta molte informazioni
strutturate rimanendo comunque facile da scansionare:
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "httpx-cache"
version = "1.2.0"
description = "Transparent HTTP caching layer for httpx"
readme = "README.md"
license = { file = "LICENSE" }
requires-python = ">=3.9"
authors = [
{ name = "Ada Lovelace", email = "[email protected]" },
]
keywords = ["http", "cache", "httpx", "async"]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
]
dependencies = [
"httpx>=0.25.0",
"anyio>=4.0.0",
]
[project.optional-dependencies]
redis = ["redis>=5.0.0"]
dev = [
"pytest>=7.4.0",
"pytest-asyncio>=0.23.0",
"coverage[toml]>=7.3.0",
"ruff>=0.1.0",
"mypy>=1.7.0",
]
[project.urls]
Homepage = "https://github.com/example/httpx-cache"
Changelog = "https://github.com/example/httpx-cache/blob/main/CHANGELOG.md"
[tool.ruff]
line-length = 100
target-version = "py39"
[tool.ruff.lint]
select = ["E", "F", "I", "UP"]
[tool.mypy]
strict = true
python_version = "3.9"
[tool.coverage.run]
source = ["httpx_cache"]
branch = true
[tool.coverage.report]
fail_under = 90Questo è TOML reale che fa lavoro reale. Ogni sezione [tool.x] è uno spazio dei
nomi separato per uno strumento diverso — ruff, mypy, coverage — che vivono tutti in un file senza
interferire l'uno con l'altro. Non è necessaria nidificazione profonda, tutto è leggibile a colpo d'occhio.
Conclusione
TOML mantiene la sua promessa: leggibile, non ambiguo e tipizzato in modo pulito. Se stai avviando un nuovo progetto in Rust, Python o Go, TOML vale la pena essere il predefinito per i file di configurazione — specialmente i file che verranno committati nel controllo del codice sorgente e modificati da più persone. La sola mancanza di sensibilità agli spazi bianchi lo rende un sollievo rispetto a YAML. Per lavorare direttamente con file TOML nel tuo browser, il TOML Formatter e il TOML Validator gestiscono le attività più comuni. E se stai migrando un progetto esistente da JSON o hai bisogno di collegare TOML a una pipeline basata su JSON, i convertitori TOML to JSON e JSON to TOML ti coprono.