]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-146171: Fix nested AttributeError suggestions (#146188)
authorPablo Galindo Salgado <Pablogsal@gmail.com>
Fri, 20 Mar 2026 12:47:59 +0000 (12:47 +0000)
committerGitHub <noreply@github.com>
Fri, 20 Mar 2026 12:47:59 +0000 (12:47 +0000)
Lib/test/test_traceback.py
Lib/traceback.py
Misc/NEWS.d/next/Library/2026-03-20-00-28-00.gh-issue-146171.P5Jk2R7v.rst [new file with mode: 0644]

index 14a08995bf127c43a17fe9dc0465b4fc29628211..7124e49b22e361c20f48fdf48e5b271139c36020 100644 (file)
@@ -4420,19 +4420,21 @@ class SuggestionFormattingTestBase(SuggestionFormattingTestMixin):
         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
index 956cab49131990c0bc74fc2699af660e89f784d9..56a72ce7f5b29354f1b1e13f7b2ae51e133903c8 100644 (file)
@@ -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 (file)
index 0000000..9514085
--- /dev/null
@@ -0,0 +1,2 @@
+Nested :exc:`AttributeError` suggestions now include property-backed
+attributes on nested objects without executing the property getter.