Skip to content

Commit e1eb6f4

Browse files
committed
Address vstinner review: frozendict wrapper, types.NoneType, doctest
1. Restore frozendict() wrapper for _CROSS_LANGUAGE_HINTS table (lost during the isinstance() refactor in e2c12ec). Convert inner lists to tuples for consistency with immutable container. 2. Replace type(None) with types.NoneType per review. 3. Replace contrived None.keys() doctest with a realistic example: lst = [3, 1, 2].sort() followed by lst.pop(), showing the common "method returned None" mistake vstinner suggested.
1 parent 564ca3a commit e1eb6f4

2 files changed

Lines changed: 45 additions & 43 deletions

File tree

Doc/whatsnew/3.15.rst

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -457,15 +457,17 @@ Improved error messages
457457
...
458458
AttributeError: 'tuple' object has no attribute 'append'. Did you mean to use a 'list' object?
459459

460-
When a common method is called on ``None``, the hint suggests the type
461-
the user likely expected:
460+
When a common method is called on ``None`` (often because a method like
461+
:meth:`list.sort` returned ``None`` instead of the expected object), the
462+
hint suggests the type the user likely expected:
462463

463464
.. doctest::
464465

465-
>>> None.keys() # doctest: +ELLIPSIS
466+
>>> lst = [3, 1, 2].sort()
467+
>>> lst.pop() # doctest: +ELLIPSIS
466468
Traceback (most recent call last):
467469
...
468-
AttributeError: 'NoneType' object has no attribute 'keys'. Did you expect a 'dict'?
470+
AttributeError: 'NoneType' object has no attribute 'pop'. Did you expect a 'list' or 'dict'?
469471

470472
These hints also work for subclasses of builtin types.
471473

Lib/traceback.py

Lines changed: 39 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1673,55 +1673,55 @@ def print(self, *, file=None, chain=True, **kwargs):
16731673
# If is_raw is True, the suggestion is rendered as-is.
16741674
#
16751675
# See https://github.com/python/cpython/issues/146406.
1676-
_CROSS_LANGUAGE_HINTS = {
1676+
_CROSS_LANGUAGE_HINTS = frozendict({
16771677
# list -- JavaScript/Ruby equivalents
1678-
"push": [(list, "append", False)],
1679-
"concat": [(list, "extend", False)],
1678+
"push": ((list, "append", False),),
1679+
"concat": ((list, "extend", False),),
16801680
# list -- Java/C# equivalents
1681-
"addAll": [(list, "extend", False)],
1682-
"contains": [(list, "Use 'x in list'.", True)],
1681+
"addAll": ((list, "extend", False),),
1682+
"contains": ((list, "Use 'x in list'.", True),),
16831683
# list -- wrong-type suggestion (user expected a set)
1684-
"add": [(list, "Did you mean to use a 'set' object?", True),
1685-
(frozenset, "Did you mean to use a 'set' object?", True)],
1684+
"add": ((list, "Did you mean to use a 'set' object?", True),
1685+
(frozenset, "Did you mean to use a 'set' object?", True)),
16861686
# str -- JavaScript equivalents
1687-
"toUpperCase": [(str, "upper", False)],
1688-
"toLowerCase": [(str, "lower", False)],
1689-
"trimStart": [(str, "lstrip", False)],
1690-
"trimEnd": [(str, "rstrip", False)],
1687+
"toUpperCase": ((str, "upper", False),),
1688+
"toLowerCase": ((str, "lower", False),),
1689+
"trimStart": ((str, "lstrip", False),),
1690+
"trimEnd": ((str, "rstrip", False),),
16911691
# dict -- Java/JavaScript equivalents
1692-
"keySet": [(dict, "keys", False)],
1693-
"entrySet": [(dict, "items", False)],
1694-
"entries": [(dict, "items", False)],
1695-
"putAll": [(dict, "update", False)],
1696-
"put": [(dict, "Use d[k] = v.", True)],
1692+
"keySet": ((dict, "keys", False),),
1693+
"entrySet": ((dict, "items", False),),
1694+
"entries": ((dict, "items", False),),
1695+
"putAll": ((dict, "update", False),),
1696+
"put": ((dict, "Use d[k] = v.", True),),
16971697
# tuple -- mutable method on immutable type (user expected a list)
1698-
"append": [(tuple, "Did you mean to use a 'list' object?", True)],
1699-
"extend": [(tuple, "Did you mean to use a 'list' object?", True)],
1700-
"insert": [(tuple, "Did you mean to use a 'list' object?", True)],
1701-
"remove": [(tuple, "Did you mean to use a 'list' object?", True),
1702-
(frozenset, "Did you mean to use a 'set' object?", True)],
1698+
"append": ((tuple, "Did you mean to use a 'list' object?", True),),
1699+
"extend": ((tuple, "Did you mean to use a 'list' object?", True),),
1700+
"insert": ((tuple, "Did you mean to use a 'list' object?", True),),
1701+
"remove": ((tuple, "Did you mean to use a 'list' object?", True),
1702+
(frozenset, "Did you mean to use a 'set' object?", True)),
17031703
# frozenset -- mutable method on immutable type (user expected a set)
1704-
"discard": [(frozenset, "Did you mean to use a 'set' object?", True)],
1704+
"discard": ((frozenset, "Did you mean to use a 'set' object?", True),),
17051705
# frozendict -- mutable method on immutable type (user expected a dict)
1706-
"update": [(frozenset, "Did you mean to use a 'set' object?", True),
1707-
(frozendict, "Did you mean to use a 'dict' object?", True)],
1706+
"update": ((frozenset, "Did you mean to use a 'set' object?", True),
1707+
(frozendict, "Did you mean to use a 'dict' object?", True)),
17081708
# float -- bitwise operators belong to int
1709-
"__or__": [(float, "Did you mean to use an 'int' object? Bitwise operators are not supported by 'float'.", True)],
1710-
"__and__": [(float, "Did you mean to use an 'int' object? Bitwise operators are not supported by 'float'.", True)],
1711-
"__xor__": [(float, "Did you mean to use an 'int' object? Bitwise operators are not supported by 'float'.", True)],
1712-
"__lshift__": [(float, "Did you mean to use an 'int' object? Bitwise operators are not supported by 'float'.", True)],
1713-
"__rshift__": [(float, "Did you mean to use an 'int' object? Bitwise operators are not supported by 'float'.", True)],
1709+
"__or__": ((float, "Did you mean to use an 'int' object? Bitwise operators are not supported by 'float'.", True),),
1710+
"__and__": ((float, "Did you mean to use an 'int' object? Bitwise operators are not supported by 'float'.", True),),
1711+
"__xor__": ((float, "Did you mean to use an 'int' object? Bitwise operators are not supported by 'float'.", True),),
1712+
"__lshift__": ((float, "Did you mean to use an 'int' object? Bitwise operators are not supported by 'float'.", True),),
1713+
"__rshift__": ((float, "Did you mean to use an 'int' object? Bitwise operators are not supported by 'float'.", True),),
17141714
# NoneType -- common methods tried on None (got None instead of expected type)
1715-
"keys": [(type(None), "Did you expect a 'dict'?", True)],
1716-
"values": [(type(None), "Did you expect a 'dict'?", True)],
1717-
"items": [(type(None), "Did you expect a 'dict'?", True)],
1718-
"upper": [(type(None), "Did you expect a 'str'?", True)],
1719-
"lower": [(type(None), "Did you expect a 'str'?", True)],
1720-
"strip": [(type(None), "Did you expect a 'str'?", True)],
1721-
"split": [(type(None), "Did you expect a 'str'?", True)],
1722-
"sort": [(type(None), "Did you expect a 'list'?", True)],
1723-
"pop": [(type(None), "Did you expect a 'list' or 'dict'?", True)],
1724-
}
1715+
"keys": ((types.NoneType, "Did you expect a 'dict'?", True),),
1716+
"values": ((types.NoneType, "Did you expect a 'dict'?", True),),
1717+
"items": ((types.NoneType, "Did you expect a 'dict'?", True),),
1718+
"upper": ((types.NoneType, "Did you expect a 'str'?", True),),
1719+
"lower": ((types.NoneType, "Did you expect a 'str'?", True),),
1720+
"strip": ((types.NoneType, "Did you expect a 'str'?", True),),
1721+
"split": ((types.NoneType, "Did you expect a 'str'?", True),),
1722+
"sort": ((types.NoneType, "Did you expect a 'list'?", True),),
1723+
"pop": ((types.NoneType, "Did you expect a 'list' or 'dict'?", True),),
1724+
})
17251725

17261726

17271727
def _substitution_cost(ch_a, ch_b):

0 commit comments

Comments
 (0)