Skip to content

Commit 2fbf387

Browse files
buckbaskingvanrossum
authored andcommitted
Fix #2070 - bytes formatting incorrect in python 3 (#2168)
1 parent fde83d5 commit 2fbf387

File tree

3 files changed

+33
-6
lines changed

3 files changed

+33
-6
lines changed

mypy/checkexpr.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -965,8 +965,9 @@ def visit_op_expr(self, e: OpExpr) -> Type:
965965
if e.op == '*' and isinstance(e.left, ListExpr):
966966
# Expressions of form [...] * e get special type inference.
967967
return self.check_list_multiply(e)
968-
if e.op == '%' and isinstance(e.left, (StrExpr, BytesExpr)):
969-
return self.strfrm_checker.check_str_interpolation(cast(StrExpr, e.left), e.right)
968+
if e.op == '%':
969+
if isinstance(e.left, (StrExpr, BytesExpr, UnicodeExpr)):
970+
return self.strfrm_checker.check_str_interpolation(e.left, e.right)
970971
left_type = self.accept(e.left)
971972

972973
if e.op in nodes.op_methods:

mypy/checkstrformat.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22

33
import re
44

5-
from typing import cast, List, Tuple, Dict, Callable
5+
from typing import cast, List, Tuple, Dict, Callable, Union
66

77
from mypy.types import (
88
Type, AnyType, TupleType, Instance, UnionType
99
)
1010
from mypy.nodes import (
11-
Node, StrExpr, BytesExpr, TupleExpr, DictExpr, Context
11+
Node, StrExpr, BytesExpr, UnicodeExpr, TupleExpr, DictExpr, Context
1212
)
1313
if False:
1414
# break import cycle only needed for mypy
@@ -55,7 +55,12 @@ def __init__(self,
5555
self.exprchk = exprchk
5656
self.msg = msg
5757

58-
def check_str_interpolation(self, str: StrExpr, replacements: Node) -> Type:
58+
# TODO: In Python 3, the bytes formatting has a more restricted set of options
59+
# compared to string formatting.
60+
# TODO: Bytes formatting in Python 3 is only supported in 3.5 and up.
61+
def check_str_interpolation(self,
62+
str: Union[StrExpr, BytesExpr, UnicodeExpr],
63+
replacements: Node) -> Type:
5964
"""Check the types of the 'replacements' in a string interpolation
6065
expression: str % replacements
6166
"""
@@ -67,7 +72,15 @@ def check_str_interpolation(self, str: StrExpr, replacements: Node) -> Type:
6772
self.check_mapping_str_interpolation(specifiers, replacements)
6873
else:
6974
self.check_simple_str_interpolation(specifiers, replacements)
70-
return self.named_type('builtins.str')
75+
76+
if isinstance(str, BytesExpr):
77+
return self.named_type('builtins.bytes')
78+
elif isinstance(str, UnicodeExpr):
79+
return self.named_type('builtins.unicode')
80+
elif isinstance(str, StrExpr):
81+
return self.named_type('builtins.str')
82+
else:
83+
assert False
7184

7285
def parse_conversion_specifiers(self, format: str) -> List[ConversionSpecifier]:
7386
key_regex = r'(\(([^()]*)\))?' # (optional) parenthesised sequence of characters

test-data/unit/check-expressions.test

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1111,6 +1111,19 @@ main:4: error: Incompatible types in string interpolation (expression has type "
11111111
[case testStringInterpolationSpaceKey]
11121112
'%( )s' % {' ': 'foo'}
11131113

1114+
[case testByteByteInterpolation]
1115+
def foo(a: bytes, b: bytes):
1116+
b'%s:%s' % (a, b)
1117+
foo(b'a', b'b') == b'a:b'
1118+
1119+
[case testBytePercentInterpolationSupported]
1120+
b'%s' % (b'xyz',)
1121+
b'%(name)s' % {'name': 'jane'}
1122+
b'%c' % (123)
1123+
1124+
[case testUnicodeInterpolation_python2]
1125+
u'%s' % (u'abc',)
1126+
11141127
-- Lambdas
11151128
-- -------
11161129

0 commit comments

Comments
 (0)