diff options
author | Pablo Galindo <Pablogsal@gmail.com> | 2021-04-14 01:36:07 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-04-14 01:36:07 (GMT) |
commit | 37494b441aced0362d7edd2956ab3ea7801e60c8 (patch) | |
tree | 45d9355cde5f7c71924e8df0f16ea2e0ad63eea8 /Lib/test | |
parent | 3bc694d5f3d4eb2e5d2f0b83e498b19662845d4e (diff) | |
download | cpython-37494b441aced0362d7edd2956ab3ea7801e60c8.zip cpython-37494b441aced0362d7edd2956ab3ea7801e60c8.tar.gz cpython-37494b441aced0362d7edd2956ab3ea7801e60c8.tar.bz2 |
bpo-38530: Offer suggestions on AttributeError (#16856)
When printing AttributeError, PyErr_Display will offer suggestions of similar
attribute names in the object that the exception was raised from:
>>> collections.namedtoplo
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: module 'collections' has no attribute 'namedtoplo'. Did you mean: namedtuple?
Diffstat (limited to 'Lib/test')
-rw-r--r-- | Lib/test/test_exceptions.py | 159 |
1 files changed, 159 insertions, 0 deletions
diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 9dc3a81..e1a5ec7 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -1414,6 +1414,165 @@ class ExceptionTests(unittest.TestCase): gc_collect() +class AttributeErrorTests(unittest.TestCase): + def test_attributes(self): + # Setting 'attr' should not be a problem. + exc = AttributeError('Ouch!') + self.assertIsNone(exc.name) + self.assertIsNone(exc.obj) + + sentinel = object() + exc = AttributeError('Ouch', name='carry', obj=sentinel) + self.assertEqual(exc.name, 'carry') + self.assertIs(exc.obj, sentinel) + + def test_getattr_has_name_and_obj(self): + class A: + blech = None + + obj = A() + try: + obj.bluch + except AttributeError as exc: + self.assertEqual("bluch", exc.name) + self.assertEqual(obj, exc.obj) + + def test_getattr_has_name_and_obj_for_method(self): + class A: + def blech(self): + return + + obj = A() + try: + obj.bluch() + except AttributeError as exc: + self.assertEqual("bluch", exc.name) + self.assertEqual(obj, exc.obj) + + def test_getattr_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 + + for cls, suggestion in [(Substitution, "blech?"), + (Elimination, "blch?"), + (Addition, "bluchin?"), + (EliminationOverAddition, "bluc?"), + (SubstitutionOverElimination, "blach?"), + (SubstitutionOverAddition, "blach?")]: + try: + cls().bluch + except AttributeError as exc: + with support.captured_stderr() as err: + sys.__excepthook__(*sys.exc_info()) + + self.assertIn(suggestion, err.getvalue()) + + def test_getattr_suggestions_do_not_trigger_for_long_attributes(self): + class A: + blech = None + + try: + A().somethingverywrong + except AttributeError as exc: + with support.captured_stderr() as err: + sys.__excepthook__(*sys.exc_info()) + + self.assertNotIn("blech", err.getvalue()) + + def test_getattr_suggestions_do_not_trigger_for_big_dicts(self): + class A: + blech = None + # A class with a very big __dict__ will not be consider + # for suggestions. + for index in range(101): + setattr(A, f"index_{index}", None) + + try: + A().bluch + except AttributeError as exc: + with support.captured_stderr() as err: + sys.__excepthook__(*sys.exc_info()) + + self.assertNotIn("blech", err.getvalue()) + + def test_getattr_suggestions_no_args(self): + class A: + blech = None + def __getattr__(self, attr): + raise AttributeError() + + try: + A().bluch + except AttributeError as exc: + with support.captured_stderr() as err: + sys.__excepthook__(*sys.exc_info()) + + self.assertIn("blech", err.getvalue()) + + class A: + blech = None + def __getattr__(self, attr): + raise AttributeError + + try: + A().bluch + except AttributeError as exc: + with support.captured_stderr() as err: + sys.__excepthook__(*sys.exc_info()) + + self.assertIn("blech", err.getvalue()) + + def test_getattr_suggestions_invalid_args(self): + class NonStringifyClass: + __str__ = None + __repr__ = None + + class A: + blech = None + def __getattr__(self, attr): + raise AttributeError(NonStringifyClass()) + + class B: + blech = None + def __getattr__(self, attr): + raise AttributeError("Error", 23) + + class C: + blech = None + def __getattr__(self, attr): + raise AttributeError(23) + + for cls in [A, B, C]: + try: + cls().bluch + except AttributeError as exc: + with support.captured_stderr() as err: + sys.__excepthook__(*sys.exc_info()) + + self.assertIn("blech", err.getvalue()) + + class ImportErrorTests(unittest.TestCase): def test_attributes(self): |