From 423d7779b6eaec9d62c270870a7f8937270995b0 Mon Sep 17 00:00:00 2001 From: Pranjal095 Date: Sat, 22 Feb 2025 17:42:43 +0530 Subject: [PATCH 1/3] gh-130428: Add tests for delattr suggestions --- Lib/test/test_traceback.py | 98 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 68c4fa117a90f5..115eda84c134a2 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -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): + 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 From 813c898e5fcee3e31d5d0f995c2298f305e3f39a Mon Sep 17 00:00:00 2001 From: Pranjal095 Date: Sun, 23 Feb 2025 02:00:18 +0530 Subject: [PATCH 2/3] Refactored getattr and delattr tests --- Lib/test/test_traceback.py | 193 +++++++++++++++---------------------- 1 file changed, 77 insertions(+), 116 deletions(-) diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 115eda84c134a2..fa94726c4047f2 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -3976,7 +3976,7 @@ def callable(): ) return result_lines[0] - def test_getattr_suggestions(self): + def run_suggestion_tests(self, operation): class Substitution: noise = more_noise = a = bc = None blech = None @@ -4016,44 +4016,87 @@ class CaseChangeOverSubstitution: (EliminationOverAddition, "'bluc'?"), (CaseChangeOverSubstitution, "'BLuch'?"), ]: - actual = self.get_suggestion(cls(), 'bluch') + obj = cls() + + if operation == "getattr": + actual = self.get_suggestion(obj, 'bluch') + elif operation == "delattr": + actual = self.get_suggestion(lambda: delattr(obj, 'bluch')) + self.assertIn(suggestion, actual) - def test_getattr_suggestions_underscored(self): + def test_getattr_suggestions(self): + self.run_suggestion_tests("getattr") + + def test_delattr_suggestions(self): + self.run_suggestion_tests("delattr") + + def run_underscored_tests(self, operation): class A: bluch = None - self.assertIn("'bluch'", self.get_suggestion(A(), 'blach')) - self.assertIn("'bluch'", self.get_suggestion(A(), '_luch')) - self.assertIn("'bluch'", self.get_suggestion(A(), '_bluch')) + obj = A() + if operation == "getattr": + self.assertIn("'bluch'", self.get_suggestion(obj, 'blach')) + self.assertIn("'bluch'", self.get_suggestion(obj, '_luch')) + self.assertIn("'bluch'", self.get_suggestion(obj, '_bluch')) + elif operation == "delattr": + 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 def method(self, name): getattr(self, name) - self.assertIn("'_bluch'", self.get_suggestion(B(), '_blach')) - self.assertIn("'_bluch'", self.get_suggestion(B(), '_luch')) - self.assertNotIn("'_bluch'", self.get_suggestion(B(), 'bluch')) + obj = B() + if operation == "getattr": + self.assertIn("'_bluch'", self.get_suggestion(obj, '_blach')) + self.assertIn("'_bluch'", self.get_suggestion(obj, '_luch')) + self.assertNotIn("'_bluch'", self.get_suggestion(obj, 'bluch')) + self.assertIn("'_bluch'", self.get_suggestion(partial(obj.method, '_blach'))) + self.assertIn("'_bluch'", self.get_suggestion(partial(obj.method, '_luch'))) + self.assertIn("'_bluch'", self.get_suggestion(partial(obj.method, 'bluch'))) + elif operation == "delattr": + 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'))) - self.assertIn("'_bluch'", self.get_suggestion(partial(B().method, '_blach'))) - self.assertIn("'_bluch'", self.get_suggestion(partial(B().method, '_luch'))) - self.assertIn("'_bluch'", self.get_suggestion(partial(B().method, 'bluch'))) + def test_getattr_suggestions_underscored(self): + self.run_underscored_tests("getattr") - def test_getattr_suggestions_do_not_trigger_for_long_attributes(self): + def test_delattr_suggestions_underscored(self): + self.run_underscored_tests("delattr") + + def run_do_not_trigger_for_long_attributes_tests(self, operation): class A: blech = None - actual = self.get_suggestion(A(), 'somethingverywrong') + obj = A() + if operation == "getattr": + actual = self.get_suggestion(obj, 'somethingverywrong') + elif operation == "delattr": + actual = self.get_suggestion(lambda: delattr(obj, 'somethingverywrong')) self.assertNotIn("blech", actual) - def test_getattr_error_bad_suggestions_do_not_trigger_for_small_names(self): + def test_getattr_suggestions_do_not_trigger_for_long_attributes(self): + self.run_do_not_trigger_for_long_attributes_tests("getattr") + + def test_delattr_suggestions_do_not_trigger_for_long_attributes(self): + self.run_do_not_trigger_for_long_attributes_tests("delattr") + + def run_do_not_trigger_for_small_names_tests(self, operation): 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(MyClass, name) + if operation == "getattr": + actual = self.get_suggestion(MyClass, name) + elif operation == "delattr": + actual = self.get_suggestion(lambda: delattr(obj, name)) self.assertNotIn("Did you mean", actual) self.assertNotIn("'vvv", actual) self.assertNotIn("'mom'", actual) @@ -4061,7 +4104,13 @@ class MyClass: self.assertNotIn("'w'", actual) self.assertNotIn("'pytho'", actual) - def test_getattr_suggestions_do_not_trigger_for_big_dicts(self): + def test_getattr_error_bad_suggestions_do_not_trigger_for_small_names(self): + self.run_do_not_trigger_for_small_names_tests("getattr") + + def test_delattr_error_bad_suggestions_do_not_trigger_for_small_names(self): + self.run_do_not_trigger_for_small_names_tests("delattr") + + def run_do_not_trigger_for_big_dicts_tests(self, operation): class A: blech = None # A class with a very big __dict__ will not be considered @@ -4069,9 +4118,19 @@ class A: for index in range(2000): setattr(A, f"index_{index}", None) - actual = self.get_suggestion(A(), 'bluch') + obj = A() + if operation == "getattr": + actual = self.get_suggestion(obj, 'bluch') + elif operation == "delattr": + actual = self.get_suggestion(lambda: delattr(obj, 'bluch')) self.assertNotIn("blech", actual) + def test_getattr_suggestions_do_not_trigger_for_big_dicts(self): + self.run_do_not_trigger_for_big_dicts_tests("getattr") + + def test_delattr_suggestions_do_not_trigger_for_big_dicts(self): + self.run_do_not_trigger_for_big_dicts_tests("delattr") + def test_getattr_suggestions_no_args(self): class A: blech = None @@ -4120,104 +4179,6 @@ 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): - 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 From f980e4fab0b3baf27e04b54195927cf893b1e3e8 Mon Sep 17 00:00:00 2001 From: Pranjal095 Date: Sun, 23 Feb 2025 02:14:36 +0530 Subject: [PATCH 3/3] Added else branches to handle unrecognized operations --- Lib/test/test_traceback.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index fa94726c4047f2..36d27902e9fc8d 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -4017,12 +4017,14 @@ class CaseChangeOverSubstitution: (CaseChangeOverSubstitution, "'BLuch'?"), ]: obj = cls() - + if operation == "getattr": actual = self.get_suggestion(obj, 'bluch') elif operation == "delattr": actual = self.get_suggestion(lambda: delattr(obj, 'bluch')) - + else: + raise ValueError(f"operation '{operation}' not recognized") + self.assertIn(suggestion, actual) def test_getattr_suggestions(self): @@ -4044,6 +4046,8 @@ class 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'))) + else: + raise ValueError(f"operation '{operation}' not recognized") class B: _bluch = None @@ -4062,6 +4066,8 @@ def method(self, name): 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'))) + else: + raise ValueError(f"operation '{operation}' not recognized") def test_getattr_suggestions_underscored(self): self.run_underscored_tests("getattr") @@ -4078,6 +4084,8 @@ class A: actual = self.get_suggestion(obj, 'somethingverywrong') elif operation == "delattr": actual = self.get_suggestion(lambda: delattr(obj, 'somethingverywrong')) + else: + raise ValueError(f"operation '{operation}' not recognized") self.assertNotIn("blech", actual) def test_getattr_suggestions_do_not_trigger_for_long_attributes(self): @@ -4097,6 +4105,8 @@ class MyClass: actual = self.get_suggestion(MyClass, name) elif operation == "delattr": actual = self.get_suggestion(lambda: delattr(obj, name)) + else: + raise ValueError(f"operation '{operation}' not recognized") self.assertNotIn("Did you mean", actual) self.assertNotIn("'vvv", actual) self.assertNotIn("'mom'", actual) @@ -4123,6 +4133,8 @@ class A: actual = self.get_suggestion(obj, 'bluch') elif operation == "delattr": actual = self.get_suggestion(lambda: delattr(obj, 'bluch')) + else: + raise ValueError(f"operation '{operation}' not recognized") self.assertNotIn("blech", actual) def test_getattr_suggestions_do_not_trigger_for_big_dicts(self): @@ -4130,7 +4142,7 @@ def test_getattr_suggestions_do_not_trigger_for_big_dicts(self): def test_delattr_suggestions_do_not_trigger_for_big_dicts(self): self.run_do_not_trigger_for_big_dicts_tests("delattr") - + def test_getattr_suggestions_no_args(self): class A: blech = None