@@ -4262,6 +4262,87 @@ def __getattribute__(self, attr):
42624262 self .assertIn ("Did you mean" , actual )
42634263 self .assertIn ("bluch" , actual )
42644264
4265+ def test_getattr_nested_attribute_suggestions (self ):
4266+ # Test that nested attributes are suggested when no direct match
4267+ class Inner :
4268+ def __init__ (self ):
4269+ self .value = 42
4270+ self .data = "test"
4271+
4272+ class Outer :
4273+ def __init__ (self ):
4274+ self .inner = Inner ()
4275+
4276+ # Should suggest 'inner.value'
4277+ actual = self .get_suggestion (Outer (), 'value' )
4278+ self .assertIn ("Did you mean: 'inner.value'" , actual )
4279+
4280+ # Should suggest 'inner.data'
4281+ actual = self .get_suggestion (Outer (), 'data' )
4282+ self .assertIn ("Did you mean: 'inner.data'" , actual )
4283+
4284+ def test_getattr_nested_prioritizes_direct_matches (self ):
4285+ # Test that direct attribute matches are prioritized over nested ones
4286+ class Inner :
4287+ def __init__ (self ):
4288+ self .foo = 42
4289+
4290+ class Outer :
4291+ def __init__ (self ):
4292+ self .inner = Inner ()
4293+ self .fooo = 100 # Similar to 'foo'
4294+
4295+ # Should suggest 'fooo' (direct) not 'inner.foo' (nested)
4296+ actual = self .get_suggestion (Outer (), 'foo' )
4297+ self .assertIn ("Did you mean: 'fooo'" , actual )
4298+ self .assertNotIn ("inner.foo" , actual )
4299+
4300+ def test_getattr_nested_with_property (self ):
4301+ # Test nested suggestions work with properties
4302+ class Inner :
4303+ @property
4304+ def computed (self ):
4305+ return 42
4306+
4307+ class Outer :
4308+ def __init__ (self ):
4309+ self .inner = Inner ()
4310+
4311+ actual = self .get_suggestion (Outer (), 'computed' )
4312+ self .assertIn ("Did you mean: 'inner.computed'" , actual )
4313+
4314+ def test_getattr_nested_no_suggestion_for_deep_nesting (self ):
4315+ # Test that deeply nested attributes (2+ levels) are not suggested
4316+ class Deep :
4317+ def __init__ (self ):
4318+ self .value = 42
4319+
4320+ class Middle :
4321+ def __init__ (self ):
4322+ self .deep = Deep ()
4323+
4324+ class Outer :
4325+ def __init__ (self ):
4326+ self .middle = Middle ()
4327+
4328+ # Should not suggest 'middle.deep.value' (too deep)
4329+ actual = self .get_suggestion (Outer (), 'value' )
4330+ self .assertNotIn ("Did you mean" , actual )
4331+
4332+ def test_getattr_nested_ignores_private_attributes (self ):
4333+ # Test that nested suggestions ignore private attributes
4334+ class Inner :
4335+ def __init__ (self ):
4336+ self .public_value = 42
4337+
4338+ class Outer :
4339+ def __init__ (self ):
4340+ self ._private_inner = Inner ()
4341+
4342+ # Should not suggest '_private_inner.public_value'
4343+ actual = self .get_suggestion (Outer (), 'public_value' )
4344+ self .assertNotIn ("Did you mean" , actual )
4345+
42654346 def make_module (self , code ):
42664347 tmpdir = Path (tempfile .mkdtemp ())
42674348 self .addCleanup (shutil .rmtree , tmpdir )
0 commit comments