Skip to content

Commit 3930bbf

Browse files
TH3CHARLieJukkaL
authored andcommitted
Meaningful message with long tuple initializer (python#7995)
Resolves python#7977.
1 parent 914f0f9 commit 3930bbf

File tree

3 files changed

+117
-1
lines changed

3 files changed

+117
-1
lines changed

mypy/checker.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4062,7 +4062,9 @@ def check_subtype(self,
40624062

40634063
subtype = get_proper_type(subtype)
40644064
supertype = get_proper_type(supertype)
4065-
4065+
if self.msg.try_report_long_tuple_assignment_error(subtype, supertype, context, msg,
4066+
subtype_label, supertype_label, code=code):
4067+
return False
40664068
if self.should_suppress_optional_error([subtype]):
40674069
return False
40684070
extra_info = [] # type: List[str]

mypy/messages.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1406,6 +1406,77 @@ def print_more(self,
14061406
.format(len(conflicts) - max_items),
14071407
context, offset=offset, code=code)
14081408

1409+
def try_report_long_tuple_assignment_error(self,
1410+
subtype: ProperType,
1411+
supertype: ProperType,
1412+
context: Context,
1413+
msg: str = message_registry.INCOMPATIBLE_TYPES,
1414+
subtype_label: Optional[str] = None,
1415+
supertype_label: Optional[str] = None,
1416+
code: Optional[ErrorCode] = None) -> bool:
1417+
"""Try to generate meaningful error message for very long tuple assignment
1418+
1419+
Returns a bool: True when generating long tuple assignment error,
1420+
False when no such error reported
1421+
"""
1422+
if isinstance(subtype, TupleType):
1423+
if (len(subtype.items) > 10 and
1424+
isinstance(supertype, Instance) and
1425+
supertype.type.fullname == 'builtins.tuple'):
1426+
lhs_type = supertype.args[0]
1427+
lhs_types = [lhs_type] * len(subtype.items)
1428+
self.generate_incompatible_tuple_error(lhs_types,
1429+
subtype.items, context, msg, code)
1430+
return True
1431+
elif (isinstance(supertype, TupleType) and
1432+
(len(subtype.items) > 10 or len(supertype.items) > 10)):
1433+
if len(subtype.items) != len(supertype.items):
1434+
if supertype_label is not None and subtype_label is not None:
1435+
error_msg = "{} ({} {}, {} {})".format(msg, subtype_label,
1436+
self.format_long_tuple_type(subtype), supertype_label,
1437+
self.format_long_tuple_type(supertype))
1438+
self.fail(error_msg, context, code=code)
1439+
return True
1440+
self.generate_incompatible_tuple_error(supertype.items,
1441+
subtype.items, context, msg, code)
1442+
return True
1443+
return False
1444+
1445+
def format_long_tuple_type(self, typ: TupleType) -> str:
1446+
"""Format very long tuple type using an ellipsis notation"""
1447+
item_cnt = len(typ.items)
1448+
if item_cnt > 10:
1449+
return 'Tuple[{}, {}, ... <{} more items>]'\
1450+
.format(format_type_bare(typ.items[0]),
1451+
format_type_bare(typ.items[1]), str(item_cnt - 2))
1452+
else:
1453+
return format_type_bare(typ)
1454+
1455+
def generate_incompatible_tuple_error(self,
1456+
lhs_types: List[Type],
1457+
rhs_types: List[Type],
1458+
context: Context,
1459+
msg: str = message_registry.INCOMPATIBLE_TYPES,
1460+
code: Optional[ErrorCode] = None) -> None:
1461+
"""Generate error message for individual incompatible tuple pairs"""
1462+
error_cnt = 0
1463+
notes = [] # List[str]
1464+
for i, (lhs_t, rhs_t) in enumerate(zip(lhs_types, rhs_types)):
1465+
if not is_subtype(lhs_t, rhs_t):
1466+
if error_cnt < 3:
1467+
notes.append('Expression tuple item {} has type "{}"; "{}" expected; '
1468+
.format(str(i), format_type_bare(rhs_t), format_type_bare(lhs_t)))
1469+
error_cnt += 1
1470+
1471+
error_msg = msg + ' ({} tuple items are incompatible'.format(str(error_cnt))
1472+
if error_cnt - 3 > 0:
1473+
error_msg += '; {} items are omitted)'.format(str(error_cnt - 3))
1474+
else:
1475+
error_msg += ')'
1476+
self.fail(error_msg, context, code=code)
1477+
for note in notes:
1478+
self.note(note, context, code=code)
1479+
14091480

14101481
def quote_type_string(type_string: str) -> str:
14111482
"""Quotes a type representation for use in messages."""

test-data/unit/check-tuples.test

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1241,3 +1241,46 @@ b = ("bar", 7)
12411241
reveal_type(a + b) # N: Revealed type is 'Tuple[builtins.int, builtins.str, builtins.int, builtins.str, builtins.int]'
12421242

12431243
[builtins fixtures/tuple.pyi]
1244+
1245+
[case testAssigningWithLongTupleInitializer]
1246+
from typing import Tuple
1247+
1248+
# long initializer assignment with few mismatches
1249+
t: Tuple[int, ...] = (1, 2, 3, 4, 5, 6, 7, 8, "str", "str", "str", 11) \
1250+
# E: Incompatible types in assignment (3 tuple items are incompatible) \
1251+
# N: Expression tuple item 8 has type "str"; "int" expected; \
1252+
# N: Expression tuple item 9 has type "str"; "int" expected; \
1253+
# N: Expression tuple item 10 has type "str"; "int" expected;
1254+
1255+
# long initializer assignment with more mismatches
1256+
t1: Tuple[int, ...] = (1, 2, 3, 4, 5, 6, 7, 8, "str", "str", "str", "str") \
1257+
# E: Incompatible types in assignment (4 tuple items are incompatible; 1 items are omitted) \
1258+
# N: Expression tuple item 8 has type "str"; "int" expected; \
1259+
# N: Expression tuple item 9 has type "str"; "int" expected; \
1260+
# N: Expression tuple item 10 has type "str"; "int" expected;
1261+
1262+
# short tuple initializer assignment
1263+
t2: Tuple[int, ...] = (1, 2, "s", 4) \
1264+
# E: Incompatible types in assignment (expression has type "Tuple[int, int, str, int]", variable has type "Tuple[int, ...]")
1265+
1266+
# long initializer assignment with few mismatches, no ellipsis
1267+
t3: Tuple[int, int, int, int, int, int, int, int, int, int, int, int] = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, "str", "str") \
1268+
# E: Incompatible types in assignment (2 tuple items are incompatible) \
1269+
# N: Expression tuple item 10 has type "str"; "int" expected; \
1270+
# N: Expression tuple item 11 has type "str"; "int" expected;
1271+
1272+
# long initializer assignment with more mismatches, no ellipsis
1273+
t4: Tuple[int, int, int, int, int, int, int, int, int, int, int, int] = (1, 2, 3, 4, 5, 6, 7, 8, "str", "str", "str", "str") \
1274+
# E: Incompatible types in assignment (4 tuple items are incompatible; 1 items are omitted) \
1275+
# N: Expression tuple item 8 has type "str"; "int" expected; \
1276+
# N: Expression tuple item 9 has type "str"; "int" expected; \
1277+
# N: Expression tuple item 10 has type "str"; "int" expected;
1278+
1279+
# short tuple initializer assignment, no ellipsis
1280+
t5: Tuple[int, int] = (1, 2, "s", 4) # E: Incompatible types in assignment (expression has type "Tuple[int, int, str, int]", variable has type "Tuple[int, int]")
1281+
1282+
# long initializer assignment with mismatched pairs
1283+
t6: Tuple[int, int, int, int, int, int, int, int, int, int, int, int] = (1, 2, 3, 4, 5, 6, 7, 8, "str", "str", "str", "str", 1, 1, 1, 1, 1) \
1284+
# E: Incompatible types in assignment (expression has type Tuple[int, int, ... <15 more items>], variable has type Tuple[int, int, ... <10 more items>])
1285+
1286+
[builtins fixtures/tuple.pyi]

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy