Skip to content

Commit 284c52d

Browse files
authored
bpo-38530: Require 50% similarity in NameError and AttributeError suggestions (GH-25584)
1 parent 7244c00 commit 284c52d

File tree

2 files changed

+116
-1
lines changed

2 files changed

+116
-1
lines changed

Lib/test/test_exceptions.py

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1507,6 +1507,61 @@ def f():
15071507

15081508
self.assertNotIn("somethingverywronghehe", err.getvalue())
15091509

1510+
def test_name_error_bad_suggestions_do_not_trigger_for_small_names(self):
1511+
vvv = mom = w = id = pytho = None
1512+
1513+
with self.subTest(name="b"):
1514+
try:
1515+
b
1516+
except NameError as exc:
1517+
with support.captured_stderr() as err:
1518+
sys.__excepthook__(*sys.exc_info())
1519+
self.assertNotIn("you mean", err.getvalue())
1520+
self.assertNotIn("vvv", err.getvalue())
1521+
self.assertNotIn("mom", err.getvalue())
1522+
self.assertNotIn("'id'", err.getvalue())
1523+
self.assertNotIn("'w'", err.getvalue())
1524+
self.assertNotIn("'pytho'", err.getvalue())
1525+
1526+
with self.subTest(name="v"):
1527+
try:
1528+
v
1529+
except NameError as exc:
1530+
with support.captured_stderr() as err:
1531+
sys.__excepthook__(*sys.exc_info())
1532+
self.assertNotIn("you mean", err.getvalue())
1533+
self.assertNotIn("vvv", err.getvalue())
1534+
self.assertNotIn("mom", err.getvalue())
1535+
self.assertNotIn("'id'", err.getvalue())
1536+
self.assertNotIn("'w'", err.getvalue())
1537+
self.assertNotIn("'pytho'", err.getvalue())
1538+
1539+
with self.subTest(name="m"):
1540+
try:
1541+
m
1542+
except NameError as exc:
1543+
with support.captured_stderr() as err:
1544+
sys.__excepthook__(*sys.exc_info())
1545+
self.assertNotIn("you mean", err.getvalue())
1546+
self.assertNotIn("vvv", err.getvalue())
1547+
self.assertNotIn("mom", err.getvalue())
1548+
self.assertNotIn("'id'", err.getvalue())
1549+
self.assertNotIn("'w'", err.getvalue())
1550+
self.assertNotIn("'pytho'", err.getvalue())
1551+
1552+
with self.subTest(name="py"):
1553+
try:
1554+
py
1555+
except NameError as exc:
1556+
with support.captured_stderr() as err:
1557+
sys.__excepthook__(*sys.exc_info())
1558+
self.assertNotIn("you mean", err.getvalue())
1559+
self.assertNotIn("vvv", err.getvalue())
1560+
self.assertNotIn("mom", err.getvalue())
1561+
self.assertNotIn("'id'", err.getvalue())
1562+
self.assertNotIn("'w'", err.getvalue())
1563+
self.assertNotIn("'pytho'", err.getvalue())
1564+
15101565
def test_name_error_suggestions_do_not_trigger_for_too_many_locals(self):
15111566
def f():
15121567
# Mutating locals() is unreliable, so we need to do it by hand
@@ -1661,6 +1716,63 @@ class A:
16611716

16621717
self.assertNotIn("blech", err.getvalue())
16631718

1719+
def test_getattr_error_bad_suggestions_do_not_trigger_for_small_names(self):
1720+
class MyClass:
1721+
vvv = mom = w = id = pytho = None
1722+
1723+
with self.subTest(name="b"):
1724+
try:
1725+
MyClass.b
1726+
except AttributeError as exc:
1727+
with support.captured_stderr() as err:
1728+
sys.__excepthook__(*sys.exc_info())
1729+
self.assertNotIn("you mean", err.getvalue())
1730+
self.assertNotIn("vvv", err.getvalue())
1731+
self.assertNotIn("mom", err.getvalue())
1732+
self.assertNotIn("'id'", err.getvalue())
1733+
self.assertNotIn("'w'", err.getvalue())
1734+
self.assertNotIn("'pytho'", err.getvalue())
1735+
1736+
with self.subTest(name="v"):
1737+
try:
1738+
MyClass.v
1739+
except AttributeError as exc:
1740+
with support.captured_stderr() as err:
1741+
sys.__excepthook__(*sys.exc_info())
1742+
self.assertNotIn("you mean", err.getvalue())
1743+
self.assertNotIn("vvv", err.getvalue())
1744+
self.assertNotIn("mom", err.getvalue())
1745+
self.assertNotIn("'id'", err.getvalue())
1746+
self.assertNotIn("'w'", err.getvalue())
1747+
self.assertNotIn("'pytho'", err.getvalue())
1748+
1749+
with self.subTest(name="m"):
1750+
try:
1751+
MyClass.m
1752+
except AttributeError as exc:
1753+
with support.captured_stderr() as err:
1754+
sys.__excepthook__(*sys.exc_info())
1755+
self.assertNotIn("you mean", err.getvalue())
1756+
self.assertNotIn("vvv", err.getvalue())
1757+
self.assertNotIn("mom", err.getvalue())
1758+
self.assertNotIn("'id'", err.getvalue())
1759+
self.assertNotIn("'w'", err.getvalue())
1760+
self.assertNotIn("'pytho'", err.getvalue())
1761+
1762+
with self.subTest(name="py"):
1763+
try:
1764+
MyClass.py
1765+
except AttributeError as exc:
1766+
with support.captured_stderr() as err:
1767+
sys.__excepthook__(*sys.exc_info())
1768+
self.assertNotIn("you mean", err.getvalue())
1769+
self.assertNotIn("vvv", err.getvalue())
1770+
self.assertNotIn("mom", err.getvalue())
1771+
self.assertNotIn("'id'", err.getvalue())
1772+
self.assertNotIn("'w'", err.getvalue())
1773+
self.assertNotIn("'pytho'", err.getvalue())
1774+
1775+
16641776
def test_getattr_suggestions_do_not_trigger_for_big_dicts(self):
16651777
class A:
16661778
blech = None

Python/suggestions.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,10 @@ calculate_suggestions(PyObject *dir,
102102
if (current_distance == -1) {
103103
return NULL;
104104
}
105-
if (current_distance == 0 || current_distance > MAX_DISTANCE) {
105+
if (current_distance == 0 ||
106+
current_distance > MAX_DISTANCE ||
107+
current_distance * 2 > name_size)
108+
{
106109
continue;
107110
}
108111
if (!suggestion || current_distance < suggestion_distance) {

0 commit comments

Comments
 (0)