Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions Lib/test/test_traceback.py
Original file line number Diff line number Diff line change
Expand Up @@ -5094,6 +5094,32 @@ def func():
actual = self.get_suggestion(func)
self.assertIn("forget to import '_io'", actual)

def test_name_error_with_bad_getattr(self):
class A:
def __getattr__(self, x):
spam

actual = self.get_suggestion(A(), "eggs")
self.assertIn("name 'spam' is not defined", actual)

def test_name_error_with_bad_getattribute(self):

class A:
def __getattribute__(self, x):
spam

actual = self.get_suggestion(A(), "eggs")
self.assertIn("name 'spam' is not defined", actual)

def test_name_error_with_bad_property(self):
class A:
@property
def eggs(self):
eggs

actual = self.get_suggestion(A(), "eggs")
self.assertIn("name 'eggs' is not defined", actual)



class PurePythonSuggestionFormattingTests(
Expand Down
51 changes: 27 additions & 24 deletions Lib/traceback.py
Original file line number Diff line number Diff line change
Expand Up @@ -1922,31 +1922,34 @@ def _compute_suggestion_error(exc_value, tb, wrong_name):
return None
else:
assert isinstance(exc_value, NameError)
# find most recent frame
if tb is None:
try:
# find most recent frame
if tb is None:
return None
while tb.tb_next is not None:
tb = tb.tb_next
frame = tb.tb_frame
d = (
list(frame.f_locals)
+ list(frame.f_globals)
+ list(frame.f_builtins)
)
d = [x for x in d if isinstance(x, str)]
if not_normalized and wrong_name in d:
return wrong_name

# Check first if we are in a method and the instance
# has the wrong name as attribute
if 'self' in frame.f_locals:
self = frame.f_locals['self']
try:
has_wrong_name = hasattr(self, wrong_name)
except Exception:
has_wrong_name = False
if has_wrong_name:
return f"self.{wrong_name}"
except Exception:
return None
while tb.tb_next is not None:
tb = tb.tb_next
frame = tb.tb_frame
d = (
list(frame.f_locals)
+ list(frame.f_globals)
+ list(frame.f_builtins)
)
d = [x for x in d if isinstance(x, str)]
if not_normalized and wrong_name in d:
return wrong_name

# Check first if we are in a method and the instance
# has the wrong name as attribute
if 'self' in frame.f_locals:
self = frame.f_locals['self']
try:
has_wrong_name = hasattr(self, wrong_name)
except Exception:
has_wrong_name = False
if has_wrong_name:
return f"self.{wrong_name}"

if not_normalized and wrong_name in d:
return wrong_name
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Improve exception handling when computing suggestions in traceback.py. This fixes a bug that could cause PyREPL to exit unexpectedly.
Loading