@@ -4064,11 +4064,13 @@ def test_dont_swallow_subexceptions_of_falsey_exceptiongroup(self):
40644064global_for_suggestions = None
40654065
40664066
4067- class SuggestionFormattingTestBase :
4067+ class SuggestionFormattingTestMixin :
4068+ attr_function = getattr
4069+
40684070 def get_suggestion (self , obj , attr_name = None ):
40694071 if attr_name is not None :
40704072 def callable ():
4071- getattr (obj , attr_name )
4073+ self . attr_function (obj , attr_name )
40724074 else :
40734075 callable = obj
40744076
@@ -4077,7 +4079,9 @@ def callable():
40774079 )
40784080 return result_lines [0 ]
40794081
4080- def test_getattr_suggestions (self ):
4082+
4083+ class BaseSuggestionTests (SuggestionFormattingTestMixin ):
4084+ def test_suggestions (self ):
40814085 class Substitution :
40824086 noise = more_noise = a = bc = None
40834087 blech = None
@@ -4120,18 +4124,19 @@ class CaseChangeOverSubstitution:
41204124 actual = self .get_suggestion (cls (), 'bluch' )
41214125 self .assertIn (suggestion , actual )
41224126
4123- def test_getattr_suggestions_underscored (self ):
4127+ def test_suggestions_underscored (self ):
41244128 class A :
41254129 bluch = None
41264130
41274131 self .assertIn ("'bluch'" , self .get_suggestion (A (), 'blach' ))
41284132 self .assertIn ("'bluch'" , self .get_suggestion (A (), '_luch' ))
41294133 self .assertIn ("'bluch'" , self .get_suggestion (A (), '_bluch' ))
41304134
4135+ attr_function = self .attr_function
41314136 class B :
41324137 _bluch = None
41334138 def method (self , name ):
4134- getattr (self , name )
4139+ attr_function (self , name )
41354140
41364141 self .assertIn ("'_bluch'" , self .get_suggestion (B (), '_blach' ))
41374142 self .assertIn ("'_bluch'" , self .get_suggestion (B (), '_luch' ))
@@ -4141,28 +4146,29 @@ def method(self, name):
41414146 self .assertIn ("'_bluch'" , self .get_suggestion (partial (B ().method , '_luch' )))
41424147 self .assertIn ("'_bluch'" , self .get_suggestion (partial (B ().method , 'bluch' )))
41434148
4144- def test_getattr_suggestions_do_not_trigger_for_long_attributes (self ):
4149+
4150+ def test_do_not_trigger_for_long_attributes (self ):
41454151 class A :
41464152 blech = None
41474153
41484154 actual = self .get_suggestion (A (), 'somethingverywrong' )
41494155 self .assertNotIn ("blech" , actual )
41504156
4151- def test_getattr_error_bad_suggestions_do_not_trigger_for_small_names (self ):
4157+ def test_do_not_trigger_for_small_names (self ):
41524158 class MyClass :
41534159 vvv = mom = w = id = pytho = None
41544160
41554161 for name in ("b" , "v" , "m" , "py" ):
41564162 with self .subTest (name = name ):
4157- actual = self .get_suggestion (MyClass , name )
4163+ actual = self .get_suggestion (MyClass () , name )
41584164 self .assertNotIn ("Did you mean" , actual )
41594165 self .assertNotIn ("'vvv" , actual )
41604166 self .assertNotIn ("'mom'" , actual )
41614167 self .assertNotIn ("'id'" , actual )
41624168 self .assertNotIn ("'w'" , actual )
41634169 self .assertNotIn ("'pytho'" , actual )
41644170
4165- def test_getattr_suggestions_do_not_trigger_for_big_dicts (self ):
4171+ def test_do_not_trigger_for_big_dicts (self ):
41664172 class A :
41674173 blech = None
41684174 # A class with a very big __dict__ will not be considered
@@ -4173,7 +4179,16 @@ class A:
41734179 actual = self .get_suggestion (A (), 'bluch' )
41744180 self .assertNotIn ("blech" , actual )
41754181
4176- def test_getattr_suggestions_no_args (self ):
4182+ def test_suggestions_for_same_name (self ):
4183+ class A :
4184+ def __dir__ (self ):
4185+ return ['blech' ]
4186+ actual = self .get_suggestion (A (), 'blech' )
4187+ self .assertNotIn ("Did you mean" , actual )
4188+
4189+
4190+ class GetattrSuggestionTests (BaseSuggestionTests ):
4191+ def test_suggestions_no_args (self ):
41774192 class A :
41784193 blech = None
41794194 def __getattr__ (self , attr ):
@@ -4190,7 +4205,7 @@ def __getattr__(self, attr):
41904205 actual = self .get_suggestion (A (), 'bluch' )
41914206 self .assertIn ("blech" , actual )
41924207
4193- def test_getattr_suggestions_invalid_args (self ):
4208+ def test_suggestions_invalid_args (self ):
41944209 class NonStringifyClass :
41954210 __str__ = None
41964211 __repr__ = None
@@ -4214,13 +4229,12 @@ def __getattr__(self, attr):
42144229 actual = self .get_suggestion (cls (), 'bluch' )
42154230 self .assertIn ("blech" , actual )
42164231
4217- def test_getattr_suggestions_for_same_name (self ):
4218- class A :
4219- def __dir__ (self ):
4220- return ['blech' ]
4221- actual = self .get_suggestion (A (), 'blech' )
4222- self .assertNotIn ("Did you mean" , actual )
42234232
4233+ class DelattrSuggestionTests (BaseSuggestionTests ):
4234+ attr_function = delattr
4235+
4236+
4237+ class SuggestionFormattingTestBase (SuggestionFormattingTestMixin ):
42244238 def test_attribute_error_with_failing_dict (self ):
42254239 class T :
42264240 bluch = 1
@@ -4876,6 +4890,51 @@ class CPythonSuggestionFormattingTests(
48764890 """
48774891
48784892
4893+ class PurePythonGetattrSuggestionFormattingTests (
4894+ PurePythonExceptionFormattingMixin ,
4895+ GetattrSuggestionTests ,
4896+ unittest .TestCase ,
4897+ ):
4898+ """
4899+ Same set of tests (for attribute access) as above using the pure Python
4900+ implementation of traceback printing in traceback.py.
4901+ """
4902+
4903+
4904+ class PurePythonDelattrSuggestionFormattingTests (
4905+ PurePythonExceptionFormattingMixin ,
4906+ DelattrSuggestionTests ,
4907+ unittest .TestCase ,
4908+ ):
4909+ """
4910+ Same set of tests (for attribute deletion) as above using the pure Python
4911+ implementation of traceback printing in traceback.py.
4912+ """
4913+
4914+
4915+ @cpython_only
4916+ class CPythonGetattrSuggestionFormattingTests (
4917+ CAPIExceptionFormattingMixin ,
4918+ GetattrSuggestionTests ,
4919+ unittest .TestCase ,
4920+ ):
4921+ """
4922+ Same set of tests (for attribute access) as above but with Python's
4923+ internal traceback printing.
4924+ """
4925+
4926+
4927+ @cpython_only
4928+ class CPythonDelattrSuggestionFormattingTests (
4929+ CAPIExceptionFormattingMixin ,
4930+ DelattrSuggestionTests ,
4931+ unittest .TestCase ,
4932+ ):
4933+ """
4934+ Same set of tests (for attribute deletion) as above but with Python's
4935+ internal traceback printing.
4936+ """
4937+
48794938class MiscTest (unittest .TestCase ):
48804939
48814940 def test_all (self ):
0 commit comments