Skip to content

SCR112: format-string-mismatch

String formatting that fails at run time

Code SCR112
Name format-string-mismatch
Category Scripts (SCR)
Default severity error
Enabled by default yes
Fix available no
ign-lint equivalent none

Checks string literals formatted with % or .format(...) against their arguments, with Jython 2.7's rules (verified on Jython):

  • %: unsupported conversion characters (%q, Python 3's %a), a trailing %, a tuple with too many or too few values for the placeholders (* widths count), %(name)s with a tuple or a single value instead of a dict, %(name)s keys missing from a dict literal, %c with a string literal that is not one character ('%c' % 'ab') or an integer that is negative or beyond Unicode, a numeric conversion (%d %i %o %u %x %X %e %E %f %F %g %G) or a * width with a string, complex number, container or None literal ('%d' % 'ab'). A single non-tuple literal counts as one value.

  • str.format: a single { or }, mixing {} and {0}, conversions other than !r and !s, positional fields beyond the arguments given, and keyword fields that are not passed. A numeric field too large for a Java int ({2147483648}) is a keyword field, which only **kwargs can pass. A negative index ({-1}) always fails (ArrayIndexOutOfBoundsException; {-0} is index 0). A format spec whose width or precision is too large for a Java int ({0:2147483648}) fails for a literal argument and with !r or !s (other objects may have their own __format__), and so does one that Jython's parser rejects whatever the value: characters after the type ('{:dd}'.format(1), '{:%Y}'.format(1), '{:.2.2}'.format(1.5)) or a . without digits ('{:5.}'.format(1)). A format spec that the literal argument does not support (checked against Jython on more than five million generated specs and literals): an unknown presentation type ('{:z}'.format(1), '{:d}'.format('a'), '{:s}'.format(1), '{:d}'.format(1.5), any type but s after !r or !s); for strings (and containers and None, formatted as their str) a sign, #, = alignment (also implied by a 0 before the width) or , ('{:+s}'.format('ab')); for integers a precision with an integer type ('{:.2}'.format(1)), a sign or # with c, , with a type other than d; for floats #, , with n; for complex numbers #, zero padding, = alignment, , with n.

  • Unicode (verified on Jython on about 17,000 generated cases): a unicode format string whose format spec holds a character beyond U+00FF (u'{0:\U0001f980^4}'.format(1), also through a nested field whose literal value has one: ValueError: Cannot create PyString with non-byte value, whatever the argument); a unicode literal with non-ASCII characters made a byte string, by a byte string format ('{}'.format(u'\xe9')) or by !s (UnicodeEncodeError); a byte string with non-ASCII bytes inserted with %s into a unicode result, because the format string is unicode or a unicode value was inserted with %s or %c before it (u'%s' % '\xff', '%s %s' % (u'a', '\xff'): UnicodeDecodeError); %c of a number from 256 on into a byte string result ('%c' % 300: OverflowError). Jython formats a byte string with non-ASCII bytes into a unicode format with .format, and inserts it before any unicode value with %; those are not reported.

Only literal format strings (also concatenated with +: ('%s' + '%s') % (1,)) with literal arguments (or literal argument lists) are checked; anything computed is left alone. *args and **kwargs count when they are literals (*(1,), **{'a': 1}). Extra arguments to .format are allowed (Python ignores them).

The expression raises TypeError, ValueError, KeyError, IndexError, OverflowError or a Unicode error when it runs, typically while building a log message or an alarm text, where the failure hides the original problem.

logger.info("Line %s stopped at %s" % (line,))
label = "{} of {}".format(done)

Use instead:

logger.info("Line %s stopped at %s" % (line, stopTime))
label = "{} of {}".format(done, total)

Corresponds to E1300-E1306 and E1310-E1311/W1303-W1307 (format errors and argument mismatches). In Jython a dict on the right of % satisfies any number of positional placeholders, so iglint does not report "%s %s" % {...} as pylint does.


Generated from the rule metadata by iglint generate-docs; iglint rule SCR112 prints the same page in the terminal. Source: docs/rules/SCR112.md.