Skip to content

Commit 0085c3a

Browse files
gh-116871: Improve name suggestions in tracebacks (GH-116930)
Only include underscored names in name suggestions for AttributeError and ImportError if the original name was underscored.
1 parent d6fa1d4 commit 0085c3a

File tree

3 files changed

+45
-0
lines changed

3 files changed

+45
-0
lines changed

Lib/test/test_traceback.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3882,6 +3882,27 @@ class CaseChangeOverSubstitution:
38823882
actual = self.get_suggestion(cls(), 'bluch')
38833883
self.assertIn(suggestion, actual)
38843884

3885+
def test_getattr_suggestions_underscored(self):
3886+
class A:
3887+
bluch = None
3888+
3889+
self.assertIn("'bluch'", self.get_suggestion(A(), 'blach'))
3890+
self.assertIn("'bluch'", self.get_suggestion(A(), '_luch'))
3891+
self.assertIn("'bluch'", self.get_suggestion(A(), '_bluch'))
3892+
3893+
class B:
3894+
_bluch = None
3895+
def method(self, name):
3896+
getattr(self, name)
3897+
3898+
self.assertIn("'_bluch'", self.get_suggestion(B(), '_blach'))
3899+
self.assertIn("'_bluch'", self.get_suggestion(B(), '_luch'))
3900+
self.assertNotIn("'_bluch'", self.get_suggestion(B(), 'bluch'))
3901+
3902+
self.assertIn("'_bluch'", self.get_suggestion(partial(B().method, '_blach')))
3903+
self.assertIn("'_bluch'", self.get_suggestion(partial(B().method, '_luch')))
3904+
self.assertIn("'_bluch'", self.get_suggestion(partial(B().method, 'bluch')))
3905+
38853906
def test_getattr_suggestions_do_not_trigger_for_long_attributes(self):
38863907
class A:
38873908
blech = None
@@ -4074,6 +4095,17 @@ def test_import_from_suggestions(self):
40744095
actual = self.get_import_from_suggestion(code, 'bluch')
40754096
self.assertIn(suggestion, actual)
40764097

4098+
def test_import_from_suggestions_underscored(self):
4099+
code = "bluch = None"
4100+
self.assertIn("'bluch'", self.get_import_from_suggestion(code, 'blach'))
4101+
self.assertIn("'bluch'", self.get_import_from_suggestion(code, '_luch'))
4102+
self.assertIn("'bluch'", self.get_import_from_suggestion(code, '_bluch'))
4103+
4104+
code = "_bluch = None"
4105+
self.assertIn("'_bluch'", self.get_import_from_suggestion(code, '_blach'))
4106+
self.assertIn("'_bluch'", self.get_import_from_suggestion(code, '_luch'))
4107+
self.assertNotIn("'_bluch'", self.get_import_from_suggestion(code, 'bluch'))
4108+
40774109
def test_import_from_suggestions_do_not_trigger_for_long_attributes(self):
40784110
code = "blech = None"
40794111

Lib/traceback.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1469,12 +1469,23 @@ def _compute_suggestion_error(exc_value, tb, wrong_name):
14691469
obj = exc_value.obj
14701470
try:
14711471
d = dir(obj)
1472+
hide_underscored = (wrong_name[:1] != '_')
1473+
if hide_underscored and tb is not None:
1474+
while tb.tb_next is not None:
1475+
tb = tb.tb_next
1476+
frame = tb.tb_frame
1477+
if 'self' in frame.f_locals and frame.f_locals['self'] is obj:
1478+
hide_underscored = False
1479+
if hide_underscored:
1480+
d = [x for x in d if x[:1] != '_']
14721481
except Exception:
14731482
return None
14741483
elif isinstance(exc_value, ImportError):
14751484
try:
14761485
mod = __import__(exc_value.name)
14771486
d = dir(mod)
1487+
if wrong_name[:1] != '_':
1488+
d = [x for x in d if x[:1] != '_']
14781489
except Exception:
14791490
return None
14801491
else:
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Name suggestions for :exc:`AttributeError` and :exc:`ImportError` now only
2+
include underscored names if the original name was underscored.

0 commit comments

Comments
 (0)