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.

Formatter methodPython 2.6+Live demo
Common call
class D(string.Formatter): def get_value(self, key, args, kwargs): ...
Returns
whatever you return is formatted in place of the field
Replaces
try/except KeyError around str.format, or format_map with a __missing__ dict
Watch out
key is an int for {0} and {} but a str for {name} — handle both
Formatter.get_value(keykey — get_value: the first part of the field name — an int when it is all digits (or an automatic {} field), else a str.type: int | str · required, argsargs — The positional values given to vformat (the *args of format).type: sequence · required, kwargs) · Formatter.get_field(field_name, argsargs — The positional values given to vformat (the *args of format).type: sequence · required, kwargskwargs — The keyword values given to vformat (the **kwargs of format).type: mapping · required)
→ object · tuple[object, int | str]

Demo

Live evaluation
A subclass whose get_value fills in missing names instead of raising KeyError.
Try:
Inputs
fmtstrformat string
namestrvalue for {name}
Code
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')
Result
'Ada owes <amount?>'

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

NameTypeRequiredDescription
keyint | stryesget_value: the first part of the field name — an int when it is all digits (or an automatic {} field), else a str.
field_namestryesget_field: the whole field name as parse() returned it, e.g. '0[1].y' or 'user.name'.
argssequenceyesThe positional values given to vformat (the *args of format).
kwargsmappingyesThe 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

Dotted lookups into nested dicts
Override get_field to treat a.b.c as dictionary keys instead of attributes.
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'})
Values from several sources
Look in kwargs first, then in a fallback mapping such as settings or os.environ.
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)
Forbid attribute and index access
Only plain names — safer for format strings you did not write.
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

1. get_value with an int key
import string string.Formatter().get_value(0, ('a', 'b'), {})
Returns
'a'
2. get_value with a str key
import string string.Formatter().get_value('x', (), {'x': 1})
Returns
1
3. A digit string is a str key
import string string.Formatter().get_value('0', ('a',), {})
Returns
KeyError: '0'
4. get_field walks the steps
import string string.Formatter().get_field('0[1]', ('xy',), {})
Returns
('y', 0)
5. Attribute step
import string string.Formatter().get_field('n.real', (), {'n': 3})
Returns
(3, 'n')
6. Missing keyword
import string string.Formatter().get_field('a', (), {})
Returns
KeyError: 'a'
7. Empty attribute
import string string.Formatter().get_field('0.', ('a',), {})
Returns
ValueError: Empty attribute in format string

Pitfalls

1. Handling only str keys
An override that always looks in kwargs breaks {} and {0}: their key is an int.
kwargs only
import string
class D(string.Formatter):
    def get_value(self, key, args, kwargs):
        return kwargs.get(key, '?')
D().format('{} {x}', 'a', x='b')
'? b'
delegate ints
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')
'a b'
2. Expecting get_value to see the whole field name
For '{user.name}' get_value receives only 'user'. To supply defaults for the full path, override get_field.
get_value default
import string
class D(string.Formatter):
    def get_value(self, key, args, kwargs):
        return kwargs.get(key, '')
D().format('{user.name}')
AttributeError: 'str' object has no attribute 'name'
get_field default
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

Use it
  • Defaults for missing fields, layered value sources, case-insensitive keys — get_value
  • A different meaning for dots and brackets, or forbidding them — get_field
Reach for something else
  • One-off defaults → str.format_map with a dict subclass that defines __missing__
  • $-templates with missing names → Template.safe_substitute

Notes

CPython impl
get_field: first, rest = _string.formatter_field_name_split(field_name); obj = self.get_value(first, args, kwargs); then getattr(obj, i) or obj[i] for each step; return obj, first
Index keys
Inside [ ] an all-digit key becomes an int, anything else stays a str: {0[1]} indexes with 1, {d[key]} with 'key', {0[-1]} with the string '-1'
get_value
if isinstance(key, int): return args[key] — else return kwargs[key]; raising IndexError/KeyError is the documented contract

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__.