Formatter.parse

The parser behind str.format, exposed. Each tuple is "some literal text, then maybe one field"; {{ and }} come out as literal braces, and a malformed string raises ValueError when the iterator reaches the bad spot.

Formatter methodPython 2.6+Live demo
Common call
list(string.Formatter().parse('Hi {name}!'))
Returns
[('Hi ', 'name', '', None), ('!', None, None, None)]
Replaces
re.findall(r'{(\w+)}', s), which breaks on {{ }}, specs and indexes
Watch out
it is lazy: errors appear only when you iterate
Formatter.parse(format_stringformat_string — A format string in str.format syntax. Anything else raises TypeError.type: str · required)
→ iterator of tuple[str, str | None, str | None, str | None]

Demo

Live evaluation
Every tuple the parser yields.
Try:
Inputs
fmtstra format string
Code
import string
list(string.Formatter().parse('Hi {name!r:>10}, you owe {0:.2f}'))
Result
[('Hi ', 'name', '>10', 'r'), (', you owe ', '0', '.2f', None)]

A field with no spec gives '' for format_spec, while a piece with no field gives None — that is how you tell them apart. '{{' is returned as literal text ending in '{' (the parser splits there). parse() does not look inside a format spec: in '{x:{w}}' it reports one field x with the spec '{w}', so a fields-in-spec name like w only shows up if you parse the spec again. Empty {} fields come back with field_name '' — the numbering happens later, in vformat.

Parameters

NameTypeRequiredDescription
format_stringstryesA format string in str.format syntax. Anything else raises TypeError.

Return value

iterator of tuple[str, str | None, str | None, str | None] — One tuple per piece: the literal text before a field, then the field (or None, None, None when there is no field).

Common patterns

Which keys does a template need?
The first part of each field name (before . or [) is the argument key.
import string, re
keys = {re.split(r"[.\[]", name, 1)[0] for _, name, _, _ in string.Formatter().parse(fmt) if name}
Validate a user-supplied format string
Iterate it fully inside try: errors only appear during iteration.
import string
try:
    parts = list(string.Formatter().parse(fmt))
except ValueError as e:
    print(f"bad format string: {e}")
Rebuild the string
Reassemble pieces, e.g. after renaming fields.
import string
out = []
for lit, name, spec, conv in string.Formatter().parse(fmt):
    out.append(lit.replace("{", "{{").replace("}", "}}"))
    if name is not None:
        out.append("{" + name + ("!" + conv if conv else "") + (":" + spec if spec else "") + "}")
"".join(out)

Examples

1. Literal text only
import string list(string.Formatter().parse('plain'))
Returns
[('plain', None, None, None)]
2. A field with spec and conversion
import string list(string.Formatter().parse('Hi {name!r:>10}'))
Returns
[('Hi ', 'name', '>10', 'r')]
3. Empty string, empty list
import string list(string.Formatter().parse(''))
Returns
[]
4. Compound field names stay whole
import string list(string.Formatter().parse('{0[1].y}'))
Returns
[('', '0[1].y', '', None)]
5. Doubled braces
import string list(string.Formatter().parse('{{}}'))
Returns
[('{', None, None, None), ('}', None, None, None)]
6. Lazy: the error waits its turn
import string it = string.Formatter().parse('ok {x} } bad') next(it)
Returns
('ok ', 'x', '', None)
7. …until iteration reaches it
import string list(string.Formatter().parse('ok {x} } bad'))
Returns
ValueError: Single '}' encountered in format string
8. Unclosed field
import string list(string.Formatter().parse('{a'))
Returns
ValueError: expected '}' before end of string

Pitfalls

1. Treating every tuple as a field
Trailing literal text, and the pieces split at {{ and }}, come with field_name None. Filter on "is not None" — not on truthiness, because {} gives the empty name ''.
if name
import string
[name for _, name, _, _ in string.Formatter().parse('{}-{x}') if name]
['x']
if name is not None
import string
[name for _, name, _, _ in string.Formatter().parse('{}-{x}') if name is not None]
['', 'x']
2. Finding fields with a regular expression
A regex does not know that {{ is an escaped brace.
re.findall
import re
re.findall(r'{(\w+)}', 'use {{name}} for {name}')
['name', 'name']
Formatter.parse
import string
[n for _, n, _, _ in string.Formatter().parse('use {{name}} for {name}') if n is not None]
['name']

When to use

Use it
  • Listing the placeholders of a format string (mail merge, i18n checks)
  • Validating or rewriting format strings before using them
  • Overriding it in a subclass to support a different field syntax
Reach for something else
  • Formatting → just call format()/vformat()
  • $-templates → Template.get_identifiers()

Notes

CPython impl
return _string.formatter_parser(format_string) — the MarkupIterator from Objects/stringlib/unicode_format.h that str.format uses
Errors
Single '}' / Single '{' encountered in format string, expected '}' before end of string, unmatched '{' in format spec, expected ':' after conversion specifier, unexpected '{' in field name
Tuples
conversion is a one-character str or None; format_spec is '' when a field has none, None when there is no field

FAQ

[name for _, name, _, _ in string.Formatter().parse(s) if name is not None]. It handles {{ }}, conversions and specs correctly. Automatic fields {} appear as '' because they are numbered later.