Formatter.check_unused_args

str.format silently ignores arguments the format string never mentions. Formatter calls check_unused_args(used_args, args, kwargs) after formatting — the default does nothing, so a subclass can turn "unused" into an error.

Formatter methodPython 2.6+Live demo
Common call
def check_unused_args(self, used_args, args, kwargs): ...
Returns
None — raise to fail
Replaces
manual comparisons of parse() output against the arguments
Watch out
used_args holds ints for positional and strs for named arguments
Formatter.check_unused_args(used_argsused_args — The key of every field that was formatted: ints for positional arguments (including automatic {}), strs for keywords. Only the first part of a compound field counts — {user.name} adds 'user'.type: set[int | str] · required, argsargs — The positional arguments given to vformat.type: sequence · required, kwargskwargs — The keyword arguments given to vformat.type: mapping · required)
→ None

Demo

Live evaluation
Reject keyword arguments the format string does not use.
Try:
Inputs
fmtstrformat string using {a} and/or {b}
astrvalue for a
bstrvalue for b
Code
import string
class Strict(string.Formatter):
    def check_unused_args(self, used_args, args, kwargs):
        unused = set(kwargs) - used_args
        if unused:
            raise ValueError(f'unused: {sorted(unused)}')
Strict().format('{a} and {b}', a='tea', b='cake')
Result
'tea and cake'

used_args is a set, so a field used twice appears once. Fields inside a format spec ({a:{b}}) count too. For {a[0]} only the key a is recorded — the [0] step does not matter. The check runs after all the formatting, so a strict formatter still does the whole job before it raises.

Parameters

NameTypeRequiredDescription
used_argsset[int | str]yesThe key of every field that was formatted: ints for positional arguments (including automatic {}), strs for keywords. Only the first part of a compound field counts — {user.name} adds 'user'.
argssequenceyesThe positional arguments given to vformat.
kwargsmappingyesThe keyword arguments given to vformat.

Return value

None — The return value is ignored; raise an exception to reject the call.

Common patterns

Strict positional and keyword arguments
Fail on any argument the string does not use.
import string

class Strict(string.Formatter):
    def check_unused_args(self, used_args, args, kwargs):
        extra = [i for i in range(len(args)) if i not in used_args]
        extra += [k for k in kwargs if k not in used_args]
        if extra:
            raise ValueError(f'unused arguments: {extra}')
Warn instead of fail
Useful while migrating message catalogs.
import string, warnings

class Warn(string.Formatter):
    def check_unused_args(self, used_args, args, kwargs):
        for k in set(kwargs) - used_args:
            warnings.warn(f'unused format argument {k!r}')

Examples

1. The default does nothing
import string string.Formatter().format('{x}', x=1, extra=2)
Returns
'1'
2. It returns None
import string print(string.Formatter().check_unused_args({'x'}, (), {'x': 1, 'y': 2}))
Returns
None
3. str.format ignores extras too
'{x}'.format(x=1, extra=2)
Returns
'1'
4. A strict subclass
import string class Strict(string.Formatter): def check_unused_args(self, used_args, args, kwargs): if set(kwargs) - used_args: raise ValueError('unused keyword arguments') Strict().format('{x}', x=1, extra=2)
Returns
ValueError: unused keyword arguments
5. Positional keys are ints
import string class Spy(string.Formatter): def check_unused_args(self, used_args, args, kwargs): self.used = used_args s = Spy() s.format('{}{}{x}', 1, 2, x=3) sorted(s.used, key=str)
Returns
[0, 1, 'x']

Pitfalls

1. Comparing used_args with field names
used_args contains keys, not field names: {user.name} records 'user'. Comparing against parse() names gives false alarms.
field names
import string
fmt = '{user.name}'
names = {n for _, n, _, _ in string.Formatter().parse(fmt) if n}
names - {'user'}
{'user.name'}
keys from used_args
import string
class Spy(string.Formatter):
    def check_unused_args(self, used_args, args, kwargs):
        self.used = used_args
s = Spy()
class U:
    name = 'Ada'
s.format('{user.name}', user=U())
s.used
{'user'}
2. Forgetting positional arguments
set(kwargs) - used_args only checks keywords; unused positional values pass silently.
keywords only
import string
class Strict(string.Formatter):
    def check_unused_args(self, used_args, args, kwargs):
        if set(kwargs) - used_args:
            raise ValueError('unused')
Strict().format('{0}', 'a', 'b')
'a'
check indexes too
import string
class Strict(string.Formatter):
    def check_unused_args(self, used_args, args, kwargs):
        if set(range(len(args))) - used_args or set(kwargs) - used_args:
            raise ValueError('unused')
Strict().format('{0}', 'a', 'b')
ValueError: unused

When to use

Use it
  • Catching typos in message catalogs: a value passed but never shown usually means a misspelt field
  • APIs where every supplied argument must be used
Reach for something else
  • Formatting with large shared context dicts — most keys are unused by design

Notes

CPython impl
vformat: used_args = set(); result = self._vformat(..., used_args, 2); self.check_unused_args(used_args, args, kwargs) — the default body is pass
Timing
Called after the whole string has been formatted, and only if formatting succeeded
Keys
get_field returns (obj, first); vformat adds first to used_args — an int index or a keyword name

FAQ

No. '{x}'.format(x=1, y=2) returns '1' and y is ignored. string.Formatter calls check_unused_args at the end of vformat so that a subclass can raise instead.