Skip to content
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions Lib/test/test_traceback.py
Original file line number Diff line number Diff line change
Expand Up @@ -4120,6 +4120,104 @@ def __dir__(self):
actual = self.get_suggestion(A(), 'blech')
self.assertNotIn("Did you mean", actual)

def test_delattr_suggestions(self):
class Substitution:
noise = more_noise = a = bc = None
blech = None

class Elimination:
noise = more_noise = a = bc = None
blch = None

class Addition:
noise = more_noise = a = bc = None
bluchin = None

class SubstitutionOverElimination:
blach = None
bluc = None

class SubstitutionOverAddition:
blach = None
bluchi = None

class EliminationOverAddition:
blucha = None
bluc = None

class CaseChangeOverSubstitution:
Luch = None
fluch = None
BLuch = None

for cls, suggestion in [
(Addition, "'bluchin'?"),
(Substitution, "'blech'?"),
(Elimination, "'blch'?"),
(Addition, "'bluchin'?"),
(SubstitutionOverElimination, "'blach'?"),
(SubstitutionOverAddition, "'blach'?"),
(EliminationOverAddition, "'bluc'?"),
(CaseChangeOverSubstitution, "'BLuch'?"),
]:
obj = cls()
def callable():
delattr(obj, 'bluch')
actual = self.get_suggestion(callable)
self.assertIn(suggestion, actual)

def test_delattr_suggestions_underscored(self):
class A:
bluch = None

obj = A()
self.assertIn("'bluch'", self.get_suggestion(lambda: delattr(obj, 'blach')))
self.assertIn("'bluch'", self.get_suggestion(lambda: delattr(obj, '_luch')))
self.assertIn("'bluch'", self.get_suggestion(lambda: delattr(obj, '_bluch')))

class B:
_bluch = None

obj = B()
self.assertIn("'_bluch'", self.get_suggestion(lambda: delattr(obj, '_blach')))
self.assertIn("'_bluch'", self.get_suggestion(lambda: delattr(obj, '_luch')))
self.assertNotIn("'_bluch'", self.get_suggestion(lambda: delattr(obj, 'bluch')))

def test_delattr_suggestions_do_not_trigger_for_long_attributes(self):
class A:
blech = None

obj = A()
actual = self.get_suggestion(lambda: delattr(obj, 'somethingverywrong'))
self.assertNotIn("blech", actual)

def test_delattr_error_bad_suggestions_do_not_trigger_for_small_names(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test is the same as for getattr, can't it be refactored so that we use the same fixtures but with different calls to self.get_suggestion?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was actually thinking of doing that, but decided to go with duplication for better readability.
On reconsideration with your review, I believe it is redundant and should be refactored. Will update the PR accordingly.

class MyClass:
vvv = mom = w = id = pytho = None

obj = MyClass()
for name in ("b", "v", "m", "py"):
with self.subTest(name=name):
actual = self.get_suggestion(lambda: delattr(obj, name))
self.assertNotIn("Did you mean", actual)
self.assertNotIn("'vvv", actual)
self.assertNotIn("'mom'", actual)
self.assertNotIn("'id'", actual)
self.assertNotIn("'w'", actual)
self.assertNotIn("'pytho'", actual)

def test_delattr_suggestions_do_not_trigger_for_big_dicts(self):
class A:
blech = None
# A class with a very big __dict__ will not be considered
# for suggestions.
obj = A()
for index in range(2000):
setattr(obj, f"index_{index}", None)

actual = self.get_suggestion(lambda: delattr(obj, 'bluch'))
self.assertNotIn("blech", actual)

def test_attribute_error_with_failing_dict(self):
class T:
bluch = 1
Expand Down
Loading