diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 89980ae6f8573a..cb91a4cdcbae7a 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -4491,6 +4491,32 @@ def foo(self): actual = self.get_suggestion(instance.foo) self.assertNotIn("self.blech", actual) + def test_name_error_with_getattr(self): + class A: + def __getattr__(self, x): + print(x) + print(qq) + class B: + def __getattribute__(self, x): + print(x) + print(qq) + + for instance in (A(), B()): + with self.subTest(instance=instance): + actual = self.get_suggestion(instance, "pop") + self.assertIn("name 'qq' is not defined", actual) + self.assertEqual(actual.count("NameError"), 1) + + def test_name_error_with_property_name(self): + class A: + @property + def past(self): + past + instance = A() + actual = self.get_suggestion(instance, "past") + self.assertIn("name 'past' is not defined", actual) + self.assertEqual(actual.count("NameError"), 1) + def test_unbound_local_error_does_not_match(self): def func(): something = 3 diff --git a/Lib/traceback.py b/Lib/traceback.py index 31c73efcef5a52..283488f9bcecd9 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -1520,8 +1520,11 @@ def _compute_suggestion_error(exc_value, tb, wrong_name): # has the wrong name as attribute if 'self' in frame.f_locals: self = frame.f_locals['self'] - if hasattr(self, wrong_name): - return f"self.{wrong_name}" + try: + if hasattr(self, wrong_name): + return f"self.{wrong_name}" + except NameError: + return None try: import _suggestions diff --git a/Misc/NEWS.d/next/Library/2025-02-03-16-39-35.gh-issue-129605.sN_0uq.rst b/Misc/NEWS.d/next/Library/2025-02-03-16-39-35.gh-issue-129605.sN_0uq.rst new file mode 100644 index 00000000000000..f049abb89a1374 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-02-03-16-39-35.gh-issue-129605.sN_0uq.rst @@ -0,0 +1 @@ +Fix pyrepl crash when a :exc:`NameError` occurs in the :meth:`~object.__getattr__`, :meth:`~object.__getattribute__` and any method of a class.