Formatter.convert_field / format_field
For '{x!r:>10}' vformat calls convert_field(x, 'r') and then format_field(result, '>10'). The defaults are repr/str/ascii and format(); overriding them adds conversions like !u or changes how specs are read.
Demo
import string f = string.Formatter() f.format_field(f.convert_field('café', None), '*^10')
!r and !a run before the spec, so the quotes (and the \xe9 escape that ascii() writes for é) are part of the text that gets padded. A str value has only the s presentation type, so :d fails even when the text is '42'. An unknown conversion reaches the base class and raises "Unknown conversion specifier x".
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| value | object | yes | convert_field: the object get_field found. format_field: the (converted) object to format. |
| conversion | str | None | yes | The character after ! — 'r', 's', 'a' by default — or None when the field has no conversion. |
| format_spec | str | yes | The text after : with any nested {fields} already filled in; '' when there is none. |
Return value
object · str — convert_field: the converted value (unchanged when conversion is None). format_field: the formatted text.
Common patterns
import string from decimal import Decimal class Money(string.Formatter): def format_field(self, value, format_spec): if isinstance(value, Decimal) and format_spec == 'money': return f'${value:,.2f}' return super().format_field(value, format_spec) Money().format('{0:money}', Decimal('1234.5'))
import html, string class HTMLFormatter(string.Formatter): def format_field(self, value, format_spec): return html.escape(super().format_field(value, format_spec)) HTMLFormatter().format("<p>{}</p>", "<script>")
import string class Blank(string.Formatter): def format_field(self, value, format_spec): return super().format_field('' if value is None else value, format_spec)
Examples
Pitfalls
import string class U(string.Formatter): def convert_field(self, value, conversion): return str(value).upper() if conversion == 'u' else value U().format('{0!r}', 'x')
import string class U(string.Formatter): def convert_field(self, value, conversion): if conversion == 'u': return str(value).upper() return super().convert_field(value, conversion) U().format('{0!r}', 'x')
'{0!é}'.format('a')
import string string.Formatter().format('{0!é}', 'a')
When to use
- New conversion flags (!u, !l, !j for JSON …) — convert_field
- Custom spec mini-languages, per-type formatting or escaping every value — format_field
- Formatting one class of your own → give that class a __format__ method; str.format and f-strings will use it
Notes
FAQ
str.format cannot be extended, but string.Formatter can: subclass it, override convert_field, handle your character ('u') and return super().convert_field(value, conversion) for everything else. Then call YourFormatter().format(...).