Skip to content

Commit ddf0c05

Browse files
authored
Improve bytes formatting error (#14959)
Fixes #11806 This error message was last changed in #11139 to make it more applicable to f-strings. Since users are still getting confused, it makes sense to be more explicit about what the recommended fix is. There was also an extra set of quotes and a mention of Python 3 that didn't help. I think this is still very much a useful error, it caught a bug I had recently.
1 parent 2e75cba commit ddf0c05

File tree

4 files changed

+18
-20
lines changed

4 files changed

+18
-20
lines changed

mypy/checkstrformat.py

+5-6
Original file line numberDiff line numberDiff line change
@@ -435,9 +435,9 @@ def perform_special_format_checks(
435435
actual_type, "__str__"
436436
):
437437
self.msg.fail(
438-
'On Python 3 formatting "b\'abc\'" with "{}" '
439-
'produces "b\'abc\'", not "abc"; '
440-
'use "{!r}" if this is desired behavior',
438+
'If x = b\'abc\' then f"{x}" or "{}".format(x) produces "b\'abc\'", '
439+
'not "abc". If this is desired behavior, use f"{x!r}" or "{!r}".format(x). '
440+
"Otherwise, decode the bytes",
441441
call,
442442
code=codes.STR_BYTES_PY3,
443443
)
@@ -946,9 +946,8 @@ def check_s_special_cases(self, expr: FormatStringExpr, typ: Type, context: Cont
946946
# Couple special cases for string formatting.
947947
if has_type_component(typ, "builtins.bytes"):
948948
self.msg.fail(
949-
'On Python 3 formatting "b\'abc\'" with "%s" '
950-
'produces "b\'abc\'", not "abc"; '
951-
'use "%r" if this is desired behavior',
949+
'If x = b\'abc\' then "%s" % x produces "b\'abc\'", not "abc". '
950+
'If this is desired behavior use "%r" % x. Otherwise, decode the bytes',
952951
context,
953952
code=codes.STR_BYTES_PY3,
954953
)

mypyc/test-data/run-strings.test

+1-2
Original file line numberDiff line numberDiff line change
@@ -232,8 +232,7 @@ def test_fstring_basics() -> None:
232232

233233
x = bytes([1, 2, 3, 4])
234234
# assert f'bytes: {x}' == "bytes: b'\\x01\\x02\\x03\\x04'"
235-
# error: On Python 3 formatting "b'abc'" with "{}" produces "b'abc'", not "abc";
236-
# use "{!r}" if this is desired behavior behavior
235+
# error: If x = b'abc' then f"{x}" or "{}".format(x) produces "b'abc'", not "abc". If this is desired behavior, use f"{x!r}" or "{!r}".format(x). Otherwise, decode the bytes
237236

238237
float_num = 123.4
239238
assert f'{float_num}' == '123.4'

test-data/unit/check-errorcodes.test

+2-2
Original file line numberDiff line numberDiff line change
@@ -638,8 +638,8 @@ def g() -> int:
638638
'%d' % 'no' # E: Incompatible types in string interpolation (expression has type "str", placeholder has type "Union[int, float, SupportsInt]") [str-format]
639639
'%d + %d' % (1, 2, 3) # E: Not all arguments converted during string formatting [str-format]
640640

641-
'{}'.format(b'abc') # E: On Python 3 formatting "b'abc'" with "{}" produces "b'abc'", not "abc"; use "{!r}" if this is desired behavior [str-bytes-safe]
642-
'%s' % b'abc' # E: On Python 3 formatting "b'abc'" with "%s" produces "b'abc'", not "abc"; use "%r" if this is desired behavior [str-bytes-safe]
641+
'{}'.format(b'abc') # E: If x = b'abc' then f"{x}" or "{}".format(x) produces "b'abc'", not "abc". If this is desired behavior, use f"{x!r}" or "{!r}".format(x). Otherwise, decode the bytes [str-bytes-safe]
642+
'%s' % b'abc' # E: If x = b'abc' then "%s" % x produces "b'abc'", not "abc". If this is desired behavior use "%r" % x. Otherwise, decode the bytes [str-bytes-safe]
643643
[builtins fixtures/primitives.pyi]
644644
[typing fixtures/typing-medium.pyi]
645645

test-data/unit/check-formatting.test

+10-10
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ xb: bytes
3030
xs: str
3131

3232
'%s' % xs # OK
33-
'%s' % xb # E: On Python 3 formatting "b'abc'" with "%s" produces "b'abc'", not "abc"; use "%r" if this is desired behavior
34-
'%(name)s' % {'name': b'value'} # E: On Python 3 formatting "b'abc'" with "%s" produces "b'abc'", not "abc"; use "%r" if this is desired behavior
33+
'%s' % xb # E: If x = b'abc' then "%s" % x produces "b'abc'", not "abc". If this is desired behavior use "%r" % x. Otherwise, decode the bytes
34+
'%(name)s' % {'name': b'value'} # E: If x = b'abc' then "%s" % x produces "b'abc'", not "abc". If this is desired behavior use "%r" % x. Otherwise, decode the bytes
3535
[builtins fixtures/primitives.pyi]
3636

3737
[case testStringInterpolationCount]
@@ -435,21 +435,21 @@ N = NewType('N', bytes)
435435
n: N
436436

437437
'{}'.format(a)
438-
'{}'.format(b) # E: On Python 3 formatting "b'abc'" with "{}" produces "b'abc'", not "abc"; use "{!r}" if this is desired behavior
439-
'{}'.format(x) # E: On Python 3 formatting "b'abc'" with "{}" produces "b'abc'", not "abc"; use "{!r}" if this is desired behavior
440-
'{}'.format(n) # E: On Python 3 formatting "b'abc'" with "{}" produces "b'abc'", not "abc"; use "{!r}" if this is desired behavior
438+
'{}'.format(b) # E: If x = b'abc' then f"{x}" or "{}".format(x) produces "b'abc'", not "abc". If this is desired behavior, use f"{x!r}" or "{!r}".format(x). Otherwise, decode the bytes
439+
'{}'.format(x) # E: If x = b'abc' then f"{x}" or "{}".format(x) produces "b'abc'", not "abc". If this is desired behavior, use f"{x!r}" or "{!r}".format(x). Otherwise, decode the bytes
440+
'{}'.format(n) # E: If x = b'abc' then f"{x}" or "{}".format(x) produces "b'abc'", not "abc". If this is desired behavior, use f"{x!r}" or "{!r}".format(x). Otherwise, decode the bytes
441441

442-
f'{b}' # E: On Python 3 formatting "b'abc'" with "{}" produces "b'abc'", not "abc"; use "{!r}" if this is desired behavior
443-
f'{x}' # E: On Python 3 formatting "b'abc'" with "{}" produces "b'abc'", not "abc"; use "{!r}" if this is desired behavior
444-
f'{n}' # E: On Python 3 formatting "b'abc'" with "{}" produces "b'abc'", not "abc"; use "{!r}" if this is desired behavior
442+
f'{b}' # E: If x = b'abc' then f"{x}" or "{}".format(x) produces "b'abc'", not "abc". If this is desired behavior, use f"{x!r}" or "{!r}".format(x). Otherwise, decode the bytes
443+
f'{x}' # E: If x = b'abc' then f"{x}" or "{}".format(x) produces "b'abc'", not "abc". If this is desired behavior, use f"{x!r}" or "{!r}".format(x). Otherwise, decode the bytes
444+
f'{n}' # E: If x = b'abc' then f"{x}" or "{}".format(x) produces "b'abc'", not "abc". If this is desired behavior, use f"{x!r}" or "{!r}".format(x). Otherwise, decode the bytes
445445

446446
class C(Generic[B]):
447447
x: B
448448
def meth(self) -> None:
449-
'{}'.format(self.x) # E: On Python 3 formatting "b'abc'" with "{}" produces "b'abc'", not "abc"; use "{!r}" if this is desired behavior
449+
'{}'.format(self.x) # E: If x = b'abc' then f"{x}" or "{}".format(x) produces "b'abc'", not "abc". If this is desired behavior, use f"{x!r}" or "{!r}".format(x). Otherwise, decode the bytes
450450

451451
def func(x: A) -> A:
452-
'{}'.format(x) # E: On Python 3 formatting "b'abc'" with "{}" produces "b'abc'", not "abc"; use "{!r}" if this is desired behavior
452+
'{}'.format(x) # E: If x = b'abc' then f"{x}" or "{}".format(x) produces "b'abc'", not "abc". If this is desired behavior, use f"{x!r}" or "{!r}".format(x). Otherwise, decode the bytes
453453
return x
454454

455455
'{!r}'.format(a)

0 commit comments

Comments
 (0)