summaryrefslogtreecommitdiffstats
path: root/Lib/test
diff options
context:
space:
mode:
authorPablo Galindo <Pablogsal@gmail.com>2021-04-14 01:36:07 (GMT)
committerGitHub <noreply@github.com>2021-04-14 01:36:07 (GMT)
commit37494b441aced0362d7edd2956ab3ea7801e60c8 (patch)
tree45d9355cde5f7c71924e8df0f16ea2e0ad63eea8 /Lib/test
parent3bc694d5f3d4eb2e5d2f0b83e498b19662845d4e (diff)
downloadcpython-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.py159
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):