Formatter.get_value / get_field
get_field splits "0[1].y" into the key 0 and the steps [1] and .y; get_value(0, args, kwargs) returns args[0]; the steps are applied with normal indexing and getattr. Override get_value to change where values come from, get_field to change what a field name means.
Demo
import string class Default(string.Formatter): def get_value(self, key, args, kwargs): if isinstance(key, str): return kwargs.get(key, '<' + key + '?>') return super().get_value(key, args, kwargs) Default().format('{name} owes {amount}', 'first', name='Ada')
get_field returns the object and the key it started from — 0 or 'name' — which is what vformat passes on to check_unused_args. get_value is only asked for the first part: in '{user.name}' the default for user is the string '<user?>', and .name is then looked up on that string, which fails. Positional keys are ints and go to the base class, so '{1}' still raises IndexError with only one positional value.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| key | int | str | yes | get_value: the first part of the field name — an int when it is all digits (or an automatic {} field), else a str. |
| field_name | str | yes | get_field: the whole field name as parse() returned it, e.g. '0[1].y' or 'user.name'. |
| args | sequence | yes | The positional values given to vformat (the *args of format). |
| kwargs | mapping | yes | The keyword values given to vformat (the **kwargs of format). |
Return value
object · tuple[object, int | str] — get_value: the argument. get_field: (the resolved object, the key that was used — what vformat records as "used").
Common patterns
import string class Dotted(string.Formatter): def get_field(self, field_name, args, kwargs): obj = kwargs for part in field_name.split('.'): obj = obj[part] return obj, field_name Dotted().format('{user.name} ({user.role})', user={'name': 'Ada', 'role': 'admin'})
import string class Layered(string.Formatter): def __init__(self, fallback): self.fallback = fallback def get_value(self, key, args, kwargs): if isinstance(key, str) and key not in kwargs: return self.fallback[key] return super().get_value(key, args, kwargs)
import string class Plain(string.Formatter): def get_field(self, field_name, args, kwargs): if not field_name.isidentifier(): raise ValueError(f"field not allowed: {field_name!r}") return self.get_value(field_name, args, kwargs), field_name
Examples
Pitfalls
import string class D(string.Formatter): def get_value(self, key, args, kwargs): return kwargs.get(key, '?') D().format('{} {x}', 'a', x='b')
import string class D(string.Formatter): def get_value(self, key, args, kwargs): if isinstance(key, int): return super().get_value(key, args, kwargs) return kwargs.get(key, '?') D().format('{} {x}', 'a', x='b')
import string class D(string.Formatter): def get_value(self, key, args, kwargs): return kwargs.get(key, '') D().format('{user.name}')
import string class D(string.Formatter): def get_field(self, field_name, args, kwargs): try: return super().get_field(field_name, args, kwargs) except (KeyError, IndexError, AttributeError): return '', field_name D().format('{user.name}')
When to use
- Defaults for missing fields, layered value sources, case-insensitive keys — get_value
- A different meaning for dots and brackets, or forbidding them — get_field
- One-off defaults → str.format_map with a dict subclass that defines __missing__
- $-templates with missing names → Template.safe_substitute
Notes
FAQ
Subclass string.Formatter and override get_value: return kwargs.get(key, default) for str keys and call super() for int keys. Alternatively use '...'.format_map(d) with a dict subclass that defines __missing__.