@@ -4343,6 +4343,57 @@ def __init__(self):
43434343 actual = self .get_suggestion (Outer (), 'public_value' )
43444344 self .assertNotIn ("Did you mean" , actual )
43454345
4346+ def test_getattr_nested_limits_attribute_checks (self ):
4347+ # Test that nested suggestions are limited to checking first 20 non-private attributes
4348+ class Inner :
4349+ def __init__ (self ):
4350+ self .target_value = 42
4351+
4352+ class Outer :
4353+ def __init__ (self ):
4354+ # Add many attributes before 'inner'
4355+ for i in range (25 ):
4356+ setattr (self , f'attr_{ i :02d} ' , i )
4357+ # Add the inner object after 20+ attributes
4358+ self .inner = Inner ()
4359+
4360+ obj = Outer ()
4361+ # Verify that 'inner' is indeed present but after position 20
4362+ attrs = [x for x in sorted (dir (obj )) if not x .startswith ('_' )]
4363+ inner_position = attrs .index ('inner' )
4364+ self .assertGreater (inner_position , 19 , "inner should be after position 20 in sorted attributes" )
4365+
4366+ # Should not suggest 'inner.target_value' because inner is beyond the first 20 attributes checked
4367+ actual = self .get_suggestion (obj , 'target_value' )
4368+ self .assertNotIn ("inner.target_value" , actual )
4369+
4370+ def test_getattr_nested_returns_first_match_only (self ):
4371+ # Test that only the first nested match is returned (not multiple)
4372+ class Inner1 :
4373+ def __init__ (self ):
4374+ self .value = 1
4375+
4376+ class Inner2 :
4377+ def __init__ (self ):
4378+ self .value = 2
4379+
4380+ class Inner3 :
4381+ def __init__ (self ):
4382+ self .value = 3
4383+
4384+ class Outer :
4385+ def __init__ (self ):
4386+ # Multiple inner objects with same attribute
4387+ self .a_inner = Inner1 ()
4388+ self .b_inner = Inner2 ()
4389+ self .c_inner = Inner3 ()
4390+
4391+ # Should suggest only the first match (alphabetically)
4392+ actual = self .get_suggestion (Outer (), 'value' )
4393+ self .assertIn ("'a_inner.value'" , actual )
4394+ # Verify it's a single suggestion, not multiple
4395+ self .assertEqual (actual .count ("Did you mean" ), 1 )
4396+
43464397 def make_module (self , code ):
43474398 tmpdir = Path (tempfile .mkdtemp ())
43484399 self .addCleanup (shutil .rmtree , tmpdir )
0 commit comments