https://github.com/python/cpython/commit/2f4eb34bd23d57d38f81da561e889c60fca244cd
commit: 2f4eb34bd23d57d38f81da561e889c60fca244cd
branch: main
author: Pablo Galindo Salgado <[email protected]>
committer: pablogsal <[email protected]>
date: 2026-03-20T12:47:59Z
summary:

gh-146171: Fix nested AttributeError suggestions (#146188)

files:
A Misc/NEWS.d/next/Library/2026-03-20-00-28-00.gh-issue-146171.P5Jk2R7v.rst
M Lib/test/test_traceback.py
M Lib/traceback.py

diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py
index 14a08995bf127c..7124e49b22e361 100644
--- a/Lib/test/test_traceback.py
+++ b/Lib/test/test_traceback.py
@@ -4420,19 +4420,21 @@ def __init__(self):
         self.assertNotIn("inner.foo", actual)
 
     def test_getattr_nested_with_property(self):
-        # Test that descriptors (including properties) are suggested in nested 
attributes
+        # Property suggestions should not execute the property getter.
         class Inner:
             @property
             def computed(self):
-                return 42
+                return missing_name
 
         class Outer:
             def __init__(self):
                 self.inner = Inner()
 
         actual = self.get_suggestion(Outer(), 'computed')
-        # Descriptors should not be suggested to avoid executing arbitrary code
-        self.assertIn("inner.computed", actual)
+        self.assertIn(
+            "Did you mean '.inner.computed' instead of '.computed'",
+            actual,
+        )
 
     def test_getattr_nested_no_suggestion_for_deep_nesting(self):
         # Test that deeply nested attributes (2+ levels) are not suggested
diff --git a/Lib/traceback.py b/Lib/traceback.py
index 956cab49131990..56a72ce7f5b293 100644
--- a/Lib/traceback.py
+++ b/Lib/traceback.py
@@ -1670,16 +1670,20 @@ def _check_for_nested_attribute(obj, wrong_name, attrs):
 
     Returns the first nested attribute suggestion found, or None.
     Limited to checking 20 attributes.
-    Only considers non-descriptor attributes to avoid executing arbitrary code.
+    Only considers non-descriptor outer attributes to avoid executing
+    arbitrary code. Checks nested attributes statically so descriptors such
+    as properties can still be suggested without invoking them.
     Skips lazy imports to avoid triggering module loading.
     """
+    from inspect import getattr_static
+
     # Check for nested attributes (only one level deep)
     attrs_to_check = [x for x in attrs if not x.startswith('_')][:20]  # Limit 
number of attributes to check
     for attr_name in attrs_to_check:
         with suppress(Exception):
             # Check if attr_name is a descriptor - if so, skip it
-            attr_from_class = getattr(type(obj), attr_name, None)
-            if attr_from_class is not None and hasattr(attr_from_class, 
'__get__'):
+            attr_from_class = getattr_static(type(obj), attr_name, _sentinel)
+            if attr_from_class is not _sentinel and hasattr(attr_from_class, 
'__get__'):
                 continue  # Skip descriptors to avoid executing arbitrary code
 
             # Skip lazy imports to avoid triggering module loading
@@ -1689,10 +1693,10 @@ def _check_for_nested_attribute(obj, wrong_name, attrs):
             # Safe to get the attribute since it's not a descriptor
             attr_obj = getattr(obj, attr_name)
 
-            # Check if the nested attribute exists and is not a descriptor
-            nested_attr_from_class = getattr(type(attr_obj), wrong_name, None)
+            if _is_lazy_import(attr_obj, wrong_name):
+                continue
 
-            if hasattr(attr_obj, wrong_name):
+            if getattr_static(attr_obj, wrong_name, _sentinel) is not 
_sentinel:
                 return f"{attr_name}.{wrong_name}"
 
     return None
diff --git 
a/Misc/NEWS.d/next/Library/2026-03-20-00-28-00.gh-issue-146171.P5Jk2R7v.rst 
b/Misc/NEWS.d/next/Library/2026-03-20-00-28-00.gh-issue-146171.P5Jk2R7v.rst
new file mode 100644
index 00000000000000..9514085bd3d27b
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-03-20-00-28-00.gh-issue-146171.P5Jk2R7v.rst
@@ -0,0 +1,2 @@
+Nested :exc:`AttributeError` suggestions now include property-backed
+attributes on nested objects without executing the property getter.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to