Skip to content

Commit 1fa71c4

Browse files
authored
Remove python2 logic from checkstrformat.py (#13248)
1 parent 87fcd07 commit 1fa71c4

File tree

2 files changed

+44
-80
lines changed

2 files changed

+44
-80
lines changed

mypy/checkexpr.py

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2585,15 +2585,10 @@ def visit_op_expr(self, e: OpExpr) -> Type:
25852585
# Expressions of form [...] * e get special type inference.
25862586
return self.check_list_multiply(e)
25872587
if e.op == "%":
2588-
pyversion = self.chk.options.python_version
2589-
if pyversion[0] == 3:
2590-
if isinstance(e.left, BytesExpr) and pyversion[1] >= 5:
2591-
return self.strfrm_checker.check_str_interpolation(e.left, e.right)
2592-
if isinstance(e.left, StrExpr):
2593-
return self.strfrm_checker.check_str_interpolation(e.left, e.right)
2594-
elif pyversion[0] == 2:
2595-
if isinstance(e.left, (StrExpr, BytesExpr, UnicodeExpr)):
2596-
return self.strfrm_checker.check_str_interpolation(e.left, e.right)
2588+
if isinstance(e.left, BytesExpr) and self.chk.options.python_version >= (3, 5):
2589+
return self.strfrm_checker.check_str_interpolation(e.left, e.right)
2590+
if isinstance(e.left, StrExpr):
2591+
return self.strfrm_checker.check_str_interpolation(e.left, e.right)
25972592
left_type = self.accept(e.left)
25982593

25992594
proper_left_type = get_proper_type(left_type)

mypy/checkstrformat.py

Lines changed: 40 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
StrExpr,
3939
TempNode,
4040
TupleExpr,
41-
UnicodeExpr,
4241
)
4342
from mypy.types import (
4443
AnyType,
@@ -65,7 +64,7 @@
6564
from mypy.subtypes import is_subtype
6665
from mypy.typeops import custom_special_method
6766

68-
FormatStringExpr: _TypeAlias = Union[StrExpr, BytesExpr, UnicodeExpr]
67+
FormatStringExpr: _TypeAlias = Union[StrExpr, BytesExpr]
6968
Checkers: _TypeAlias = Tuple[Callable[[Expression], None], Callable[[Type], bool]]
7069
MatchMap: _TypeAlias = Dict[Tuple[int, int], Match[str]] # span -> match
7170

@@ -331,9 +330,6 @@ def __init__(
331330
self.chk = chk
332331
self.exprchk = exprchk
333332
self.msg = msg
334-
# This flag is used to track Python 2 corner case where for example
335-
# '%s, %d' % (u'abc', 42) returns u'abc, 42' (i.e. unicode, not a string).
336-
self.unicode_upcast = False
337333

338334
def check_str_format_call(self, call: CallExpr, format_value: str) -> None:
339335
"""Perform more precise checks for str.format() calls when possible.
@@ -402,7 +398,7 @@ def check_specs_in_format_call(
402398
expected_type: Optional[Type] = AnyType(TypeOfAny.special_form)
403399
else:
404400
assert isinstance(call.callee, MemberExpr)
405-
if isinstance(call.callee.expr, (StrExpr, UnicodeExpr)):
401+
if isinstance(call.callee.expr, StrExpr):
406402
format_str = call.callee.expr
407403
else:
408404
format_str = StrExpr(format_value)
@@ -453,17 +449,16 @@ def perform_special_format_checks(
453449
if len(c_typ.value) != 1:
454450
self.msg.requires_int_or_char(call, format_call=True)
455451
if (not spec.conv_type or spec.conv_type == "s") and not spec.conversion:
456-
if self.chk.options.python_version >= (3, 0):
457-
if has_type_component(actual_type, "builtins.bytes") and not custom_special_method(
458-
actual_type, "__str__"
459-
):
460-
self.msg.fail(
461-
'On Python 3 formatting "b\'abc\'" with "{}" '
462-
'produces "b\'abc\'", not "abc"; '
463-
'use "{!r}" if this is desired behavior',
464-
call,
465-
code=codes.STR_BYTES_PY3,
466-
)
452+
if has_type_component(actual_type, "builtins.bytes") and not custom_special_method(
453+
actual_type, "__str__"
454+
):
455+
self.msg.fail(
456+
'On Python 3 formatting "b\'abc\'" with "{}" '
457+
'produces "b\'abc\'", not "abc"; '
458+
'use "{!r}" if this is desired behavior',
459+
call,
460+
code=codes.STR_BYTES_PY3,
461+
)
467462
if spec.flags:
468463
numeric_types = UnionType(
469464
[self.named_type("builtins.int"), self.named_type("builtins.float")]
@@ -706,15 +701,14 @@ def check_str_interpolation(self, expr: FormatStringExpr, replacements: Expressi
706701
self.exprchk.accept(expr)
707702
specifiers = parse_conversion_specifiers(expr.value)
708703
has_mapping_keys = self.analyze_conversion_specifiers(specifiers, expr)
709-
if isinstance(expr, BytesExpr) and (3, 0) <= self.chk.options.python_version < (3, 5):
704+
if isinstance(expr, BytesExpr) and self.chk.options.python_version < (3, 5):
710705
self.msg.fail(
711706
"Bytes formatting is only supported in Python 3.5 and later",
712707
replacements,
713708
code=codes.STRING_FORMATTING,
714709
)
715710
return AnyType(TypeOfAny.from_error)
716711

717-
self.unicode_upcast = False
718712
if has_mapping_keys is None:
719713
pass # Error was reported
720714
elif has_mapping_keys:
@@ -724,11 +718,7 @@ def check_str_interpolation(self, expr: FormatStringExpr, replacements: Expressi
724718

725719
if isinstance(expr, BytesExpr):
726720
return self.named_type("builtins.bytes")
727-
elif isinstance(expr, UnicodeExpr):
728-
return self.named_type("builtins.unicode")
729721
elif isinstance(expr, StrExpr):
730-
if self.unicode_upcast:
731-
return self.named_type("builtins.unicode")
732722
return self.named_type("builtins.str")
733723
else:
734724
assert False
@@ -815,11 +805,11 @@ def check_mapping_str_interpolation(
815805
) -> None:
816806
"""Check % string interpolation with names specifiers '%(name)s' % {'name': 'John'}."""
817807
if isinstance(replacements, DictExpr) and all(
818-
isinstance(k, (StrExpr, BytesExpr, UnicodeExpr)) for k, v in replacements.items
808+
isinstance(k, (StrExpr, BytesExpr)) for k, v in replacements.items
819809
):
820810
mapping: Dict[str, Type] = {}
821811
for k, v in replacements.items:
822-
if self.chk.options.python_version >= (3, 0) and isinstance(expr, BytesExpr):
812+
if isinstance(expr, BytesExpr):
823813
# Special case: for bytes formatting keys must be bytes.
824814
if not isinstance(k, BytesExpr):
825815
self.msg.fail(
@@ -870,21 +860,14 @@ def check_mapping_str_interpolation(
870860
def build_dict_type(self, expr: FormatStringExpr) -> Type:
871861
"""Build expected mapping type for right operand in % formatting."""
872862
any_type = AnyType(TypeOfAny.special_form)
873-
if self.chk.options.python_version >= (3, 0):
874-
if isinstance(expr, BytesExpr):
875-
bytes_type = self.chk.named_generic_type("builtins.bytes", [])
876-
return self.chk.named_generic_type("typing.Mapping", [bytes_type, any_type])
877-
elif isinstance(expr, StrExpr):
878-
str_type = self.chk.named_generic_type("builtins.str", [])
879-
return self.chk.named_generic_type("typing.Mapping", [str_type, any_type])
880-
else:
881-
assert False, "There should not be UnicodeExpr on Python 3"
882-
else:
863+
if isinstance(expr, BytesExpr):
864+
bytes_type = self.chk.named_generic_type("builtins.bytes", [])
865+
return self.chk.named_generic_type("typing.Mapping", [bytes_type, any_type])
866+
elif isinstance(expr, StrExpr):
883867
str_type = self.chk.named_generic_type("builtins.str", [])
884-
unicode_type = self.chk.named_generic_type("builtins.unicode", [])
885-
str_map = self.chk.named_generic_type("typing.Mapping", [str_type, any_type])
886-
unicode_map = self.chk.named_generic_type("typing.Mapping", [unicode_type, any_type])
887-
return UnionType.make_union([str_map, unicode_map])
868+
return self.chk.named_generic_type("typing.Mapping", [str_type, any_type])
869+
else:
870+
assert False, "There should not be UnicodeExpr on Python 3"
888871

889872
def build_replacement_checkers(
890873
self, specifiers: List[ConversionSpecifier], context: Context, expr: FormatStringExpr
@@ -979,29 +962,24 @@ def check_s_special_cases(self, expr: FormatStringExpr, typ: Type, context: Cont
979962
"""Additional special cases for %s in bytes vs string context."""
980963
if isinstance(expr, StrExpr):
981964
# Couple special cases for string formatting.
982-
if self.chk.options.python_version >= (3, 0):
983-
if has_type_component(typ, "builtins.bytes"):
984-
self.msg.fail(
985-
'On Python 3 formatting "b\'abc\'" with "%s" '
986-
'produces "b\'abc\'", not "abc"; '
987-
'use "%r" if this is desired behavior',
988-
context,
989-
code=codes.STR_BYTES_PY3,
990-
)
991-
return False
992-
if self.chk.options.python_version < (3, 0):
993-
if has_type_component(typ, "builtins.unicode"):
994-
self.unicode_upcast = True
965+
if has_type_component(typ, "builtins.bytes"):
966+
self.msg.fail(
967+
'On Python 3 formatting "b\'abc\'" with "%s" '
968+
'produces "b\'abc\'", not "abc"; '
969+
'use "%r" if this is desired behavior',
970+
context,
971+
code=codes.STR_BYTES_PY3,
972+
)
973+
return False
995974
if isinstance(expr, BytesExpr):
996975
# A special case for bytes formatting: b'%s' actually requires bytes on Python 3.
997-
if self.chk.options.python_version >= (3, 0):
998-
if has_type_component(typ, "builtins.str"):
999-
self.msg.fail(
1000-
"On Python 3 b'%s' requires bytes, not string",
1001-
context,
1002-
code=codes.STRING_FORMATTING,
1003-
)
1004-
return False
976+
if has_type_component(typ, "builtins.str"):
977+
self.msg.fail(
978+
"On Python 3 b'%s' requires bytes, not string",
979+
context,
980+
code=codes.STRING_FORMATTING,
981+
)
982+
return False
1005983
return True
1006984

1007985
def checkers_for_c_type(
@@ -1016,7 +994,7 @@ def checkers_for_c_type(
1016994

1017995
def check_type(type: Type) -> bool:
1018996
assert expected_type is not None
1019-
if self.chk.options.python_version >= (3, 0) and isinstance(format_expr, BytesExpr):
997+
if isinstance(format_expr, BytesExpr):
1020998
err_msg = '"%c" requires an integer in range(256) or a single byte'
1021999
else:
10221000
err_msg = '"%c" requires int or char'
@@ -1037,13 +1015,11 @@ def check_expr(expr: Expression) -> None:
10371015
if check_type(type):
10381016
# Python 3 doesn't support b'%c' % str
10391017
if (
1040-
self.chk.options.python_version >= (3, 0)
1041-
and isinstance(format_expr, BytesExpr)
1018+
isinstance(format_expr, BytesExpr)
10421019
and isinstance(expr, BytesExpr)
10431020
and len(expr.value) != 1
10441021
):
10451022
self.msg.requires_int_or_single_byte(context)
1046-
# In Python 2, b'%c' is the same as '%c'
10471023
elif isinstance(expr, (StrExpr, BytesExpr)) and len(expr.value) != 1:
10481024
self.msg.requires_int_or_char(context)
10491025

@@ -1079,13 +1055,6 @@ def conversion_type(
10791055
return None
10801056
return self.named_type("builtins.bytes")
10811057
elif p == "a":
1082-
if self.chk.options.python_version < (3, 0):
1083-
self.msg.fail(
1084-
'Format character "a" is only supported in Python 3',
1085-
context,
1086-
code=codes.STRING_FORMATTING,
1087-
)
1088-
return None
10891058
# TODO: return type object?
10901059
return AnyType(TypeOfAny.special_form)
10911060
elif p in ["s", "r"]:

0 commit comments

Comments
 (0)