"%(k)s" % {...} — the mapping form of %-formatting
print("%(k)s" % {"k": "v"})
Unhandled exception: ValueError: unsupported format character "("
Fails at RUN time with a named ValueError quoting the character it could not handle — visible, not a wrong string.
Positional %-formatting is complete: a sweep against CPython matched %s %d %.2f, %x/%o, %e/%g, sign flags, thousands separators and * width. The
mapping form is the remaining spelling, and it is the one logging and templating
code uses because it survives reordering.
The parenthesised NAME is the whole feature: %(name)s looks up name in the
right-hand mapping instead of consuming the next positional argument, and the
rest of the conversion (flags, width, precision, type) is unchanged — so the
existing spec handling is reused and only the argument SOURCE differs.
Gate
make test-nilpy + self-host byte-identical, CPython-diffed over a mapping with
several keys, a key used twice, a missing key (KeyError, now that a KeyError
names its key), mapping keys combined with flags/width/precision, and a literal
%% alongside.
2026-08-09 — IMPLEMENTED
The key is parsed BEFORE the flags (%(name)-10s) and the rest of the
conversion is unchanged, so the existing spec grammar is reused exactly as the
ticket expected — only the argument SOURCE differs.
The subtlety worth keeping: a mapping placeholder consumes no positional
argument, so the positional counter must not advance. "%(k)s %(k)s" is what
pins it — one key read twice, which a counter-advancing implementation gets
wrong on the second placeholder only.
A missing key raises KeyError NAMING the key, which works because the dict fetch
already raises a proper KeyError as of the same day's
bug-nilpy-exception-str-and-repr-diverge-from-cpython fix — so that case also
guards this fix from the other direction.
Verified against CPython: repeated keys, out-of-order keys, every
flag/width/precision combination through a mapping key, a literal %% beside
one, the missing-key KeyError, and the positional forms as controls.
gate.sh quick GREEN.