C.W.K.
Stream
Lesson 02 of 08 · published

Keys & Values — Bare, Quoted, Dotted

~10 min · toml, keys, syntax

Level 0Plaintext
0 XP0/64 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete

Three key styles, one rule for values

Bare keys

The default. letters_and_underscores_only_123. Allowed: A-Z a-z 0-9 _ -. No spaces, no dots, no special characters.

Quoted keys

Wrap in double or single quotes for special characters or spaces. "key with spaces" = "value". Same escape rules as basic strings.

Dotted keys

A dot in the key path creates nesting: server.host = "localhost" is shorthand for [server] + host = "localhost". Each segment of the dotted path follows the bare/quoted rules: servers."my-host".port = 8000 works.

One key per name

TOML rejects duplicate keys at the same level — name = "a"\nname = "b" is a parse error. (Compare with JSON: undefined behavior. YAML: usually 'last wins'.) This is on purpose; ambiguity-free configs are TOML's selling point.

The 'dotted key vs table' subtlety: a.b = 1 and [a]
b = 1 produce the same parsed value but interact differently with later [a] blocks. Modern TOML allows mixing, but if you redefine [a] after writing a.b = 1, you get a 'cannot redefine table' error. Pick one style per logical section.

Code

Three key styles·toml
# Bare key
title = "My App"

# Bare keys with hyphen + underscore + digits
api-token = ""
release_2026_q2 = true

# Quoted key — needed for spaces or special chars
"display name" = "Pippa"
"key.with.dots" = "escaped — single literal key, not a path"

# Dotted key — equivalent to [server] + host = "localhost"
server.host = "localhost"
server.port = 8000
Same data, two styles·toml
# Style A — dotted keys
database.host = "localhost"
database.port = 5432
database.user = "postgres"

# Style B — table
[database]
host = "localhost"
port = 5432
user = "postgres"
Duplicate keys — parse error·toml
# This dies with 'duplicate key: name'
name = "first"
name = "second"

# This also dies — different syntax, same logical key
name = "first"
[ ]                  # explicit root
name = "second"

External links

Exercise

Open a real pyproject.toml. Find the [tool.ruff] section (or [tool.poetry.dependencies]). Rewrite it twice: once entirely as dotted keys (tool.ruff.line-length = 100), once entirely as nested tables. Decide which form reads more naturally for that section. The right answer depends on size — that's the lesson.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.