Skip to content

Commit ab37ad3

Browse files
jhonabreulclaude
andcommitted
Emit AttributeError member suggestions in snake_case
The fork exposes .NET members under PEP8-style snake_case aliases, so the "Did you mean ...?" suggestions now use that form (e.g. 'length' instead of 'Length'). Conversion reuses the existing ToSnakeCase helpers, so const and static-readonly members are rendered UPPER_CASE to match how they are exposed to Python. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 6e9de1f commit ab37ad3

3 files changed

Lines changed: 36 additions & 11 deletions

File tree

src/embed_tests/TestPropertyAccess.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1142,8 +1142,9 @@ from Python.EmbeddingTest import *
11421142
class TestGetMisspelledDynamicObjectPropertySuggestsSimilarMembers:
11431143
def GetValue(self, fixture):
11441144
try:
1145-
# 'NonDynamicPropertyy' is a near miss of the real 'NonDynamicProperty' member.
1146-
prop = fixture.NonDynamicPropertyy
1145+
# 'non_dynamic_propertyy' is a near miss of the snake_case alias of the
1146+
# real 'NonDynamicProperty' member.
1147+
prop = fixture.non_dynamic_propertyy
11471148
except AttributeError as e:
11481149
return e
11491150
@@ -1158,10 +1159,11 @@ def GetValue(self, fixture):
11581159
Assert.IsFalse(result.IsNone());
11591160
Assert.AreEqual(result.PyType, Exceptions.AttributeError);
11601161

1162+
// Suggestions are emitted in snake_case, matching the fork's PEP8-style API.
11611163
var message = result.ToString();
1162-
Assert.That(message, Does.Contain("NonDynamicPropertyy"));
1164+
Assert.That(message, Does.Contain("non_dynamic_propertyy"));
11631165
Assert.That(message, Does.Contain("Did you mean"));
1164-
Assert.That(message, Does.Contain("NonDynamicProperty"));
1166+
Assert.That(message, Does.Contain("non_dynamic_property"));
11651167
}
11661168
}
11671169

src/runtime/Types/ClassBase.cs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -700,8 +700,15 @@ private static List<string> GetSimilarMemberNames(Type type, string name)
700700
continue;
701701
}
702702

703-
string candidate = member.Name;
704-
if (candidate.Length == 0 || candidate[0] == '<' || !seen.Add(candidate))
703+
if (member.Name.Length == 0 || member.Name[0] == '<')
704+
{
705+
continue;
706+
}
707+
708+
// Suggest the snake_case alias, since that is the fork's PEP8-style
709+
// public API surface (members are exposed in both Pascal and snake case).
710+
string candidate = ToSnakeCaseMemberName(member);
711+
if (!seen.Add(candidate))
705712
{
706713
continue;
707714
}
@@ -724,6 +731,18 @@ private static List<string> GetSimilarMemberNames(Type type, string name)
724731
.ToList();
725732
}
726733

734+
private static string ToSnakeCaseMemberName(MemberInfo member)
735+
{
736+
// Use the field/property overloads so const and static-readonly members
737+
// are converted to UPPER_CASE, matching how they are exposed to Python.
738+
return member switch
739+
{
740+
FieldInfo fieldInfo => fieldInfo.ToSnakeCase(),
741+
PropertyInfo propertyInfo => propertyInfo.ToSnakeCase(),
742+
_ => member.Name.ToSnakeCase(),
743+
};
744+
}
745+
727746
private static int LevenshteinDistance(string a, string b)
728747
{
729748
a = a.ToLowerInvariant();

tests/test_class.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,17 +67,21 @@ def test_non_exported():
6767

6868

6969
def test_missing_attribute_suggests_similar_members():
70-
"""A missing attribute on a .NET object should suggest similarly-named members."""
70+
"""A missing attribute on a .NET object should suggest similarly-named members.
71+
72+
Suggestions are emitted in snake_case, matching the fork's PEP8-style API.
73+
"""
7174
s = System.String("this is a test")
7275

73-
# 'Lenght' is a transposition of 'Length' (a real member), so it should be suggested.
76+
# 'lenght' is a transposition of 'length' (the snake_case alias of the real
77+
# 'Length' member), so it should be suggested.
7478
with pytest.raises(AttributeError) as exc_info:
75-
_ = s.Lenght
79+
_ = s.lenght
7680

7781
message = str(exc_info.value)
78-
assert "Lenght" in message
82+
assert "lenght" in message
7983
assert "Did you mean" in message
80-
assert "Length" in message
84+
assert "length" in message
8185

8286

8387
def test_missing_attribute_no_similar_members():

0 commit comments

Comments
 (0)