Skip to content

Commit b967068

Browse files
committed
Add test_baseexception from CPython 3.8
1 parent 176fa58 commit b967068

File tree

2 files changed

+248
-0
lines changed

2 files changed

+248
-0
lines changed

Lib/test/exception_hierarchy.txt

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
BaseException
2+
+-- SystemExit
3+
+-- KeyboardInterrupt
4+
+-- GeneratorExit
5+
+-- Exception
6+
+-- StopIteration
7+
+-- StopAsyncIteration
8+
+-- ArithmeticError
9+
| +-- FloatingPointError
10+
| +-- OverflowError
11+
| +-- ZeroDivisionError
12+
+-- AssertionError
13+
+-- AttributeError
14+
+-- BufferError
15+
+-- EOFError
16+
+-- ImportError
17+
| +-- ModuleNotFoundError
18+
+-- LookupError
19+
| +-- IndexError
20+
| +-- KeyError
21+
+-- MemoryError
22+
+-- NameError
23+
| +-- UnboundLocalError
24+
+-- OSError
25+
| +-- BlockingIOError
26+
| +-- ChildProcessError
27+
| +-- ConnectionError
28+
| | +-- BrokenPipeError
29+
| | +-- ConnectionAbortedError
30+
| | +-- ConnectionRefusedError
31+
| | +-- ConnectionResetError
32+
| +-- FileExistsError
33+
| +-- FileNotFoundError
34+
| +-- InterruptedError
35+
| +-- IsADirectoryError
36+
| +-- NotADirectoryError
37+
| +-- PermissionError
38+
| +-- ProcessLookupError
39+
| +-- TimeoutError
40+
+-- ReferenceError
41+
+-- RuntimeError
42+
| +-- NotImplementedError
43+
| +-- RecursionError
44+
+-- SyntaxError
45+
| +-- TargetScopeError
46+
| +-- IndentationError
47+
| +-- TabError
48+
+-- SystemError
49+
+-- TypeError
50+
+-- ValueError
51+
| +-- UnicodeError
52+
| +-- UnicodeDecodeError
53+
| +-- UnicodeEncodeError
54+
| +-- UnicodeTranslateError
55+
+-- Warning
56+
+-- DeprecationWarning
57+
+-- PendingDeprecationWarning
58+
+-- RuntimeWarning
59+
+-- SyntaxWarning
60+
+-- UserWarning
61+
+-- FutureWarning
62+
+-- ImportWarning
63+
+-- UnicodeWarning
64+
+-- BytesWarning
65+
+-- ResourceWarning

Lib/test/test_baseexception.py

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
import unittest
2+
import builtins
3+
import os
4+
from platform import system as platform_system
5+
6+
7+
class ExceptionClassTests(unittest.TestCase):
8+
9+
"""Tests for anything relating to exception objects themselves (e.g.,
10+
inheritance hierarchy)"""
11+
12+
def test_builtins_new_style(self):
13+
self.assertTrue(issubclass(Exception, object))
14+
15+
def verify_instance_interface(self, ins):
16+
for attr in ("args", "__str__", "__repr__"):
17+
self.assertTrue(hasattr(ins, attr),
18+
"%s missing %s attribute" %
19+
(ins.__class__.__name__, attr))
20+
21+
def test_inheritance(self):
22+
# Make sure the inheritance hierarchy matches the documentation
23+
exc_set = set()
24+
for object_ in builtins.__dict__.values():
25+
try:
26+
if issubclass(object_, BaseException):
27+
exc_set.add(object_.__name__)
28+
except TypeError:
29+
pass
30+
31+
inheritance_tree = open(os.path.join(os.path.split(__file__)[0],
32+
'exception_hierarchy.txt'))
33+
try:
34+
superclass_name = inheritance_tree.readline().rstrip()
35+
try:
36+
last_exc = getattr(builtins, superclass_name)
37+
except AttributeError:
38+
self.fail("base class %s not a built-in" % superclass_name)
39+
self.assertIn(superclass_name, exc_set,
40+
'%s not found' % superclass_name)
41+
exc_set.discard(superclass_name)
42+
superclasses = [] # Loop will insert base exception
43+
last_depth = 0
44+
for exc_line in inheritance_tree:
45+
exc_line = exc_line.rstrip()
46+
depth = exc_line.rindex('-')
47+
exc_name = exc_line[depth+2:] # Slice past space
48+
if '(' in exc_name:
49+
paren_index = exc_name.index('(')
50+
platform_name = exc_name[paren_index+1:-1]
51+
exc_name = exc_name[:paren_index-1] # Slice off space
52+
if platform_system() != platform_name:
53+
exc_set.discard(exc_name)
54+
continue
55+
if '[' in exc_name:
56+
left_bracket = exc_name.index('[')
57+
exc_name = exc_name[:left_bracket-1] # cover space
58+
try:
59+
exc = getattr(builtins, exc_name)
60+
except AttributeError:
61+
self.fail("%s not a built-in exception" % exc_name)
62+
if last_depth < depth:
63+
superclasses.append((last_depth, last_exc))
64+
elif last_depth > depth:
65+
while superclasses[-1][0] >= depth:
66+
superclasses.pop()
67+
self.assertTrue(issubclass(exc, superclasses[-1][1]),
68+
"%s is not a subclass of %s" % (exc.__name__,
69+
superclasses[-1][1].__name__))
70+
try: # Some exceptions require arguments; just skip them
71+
self.verify_instance_interface(exc())
72+
except TypeError:
73+
pass
74+
self.assertIn(exc_name, exc_set)
75+
exc_set.discard(exc_name)
76+
last_exc = exc
77+
last_depth = depth
78+
finally:
79+
inheritance_tree.close()
80+
self.assertEqual(len(exc_set), 0, "%s not accounted for" % exc_set)
81+
82+
interface_tests = ("length", "args", "str", "repr")
83+
84+
def interface_test_driver(self, results):
85+
for test_name, (given, expected) in zip(self.interface_tests, results):
86+
self.assertEqual(given, expected, "%s: %s != %s" % (test_name,
87+
given, expected))
88+
89+
def test_interface_single_arg(self):
90+
# Make sure interface works properly when given a single argument
91+
arg = "spam"
92+
exc = Exception(arg)
93+
results = ([len(exc.args), 1], [exc.args[0], arg],
94+
[str(exc), str(arg)],
95+
[repr(exc), '%s(%r)' % (exc.__class__.__name__, arg)])
96+
self.interface_test_driver(results)
97+
98+
def test_interface_multi_arg(self):
99+
# Make sure interface correct when multiple arguments given
100+
arg_count = 3
101+
args = tuple(range(arg_count))
102+
exc = Exception(*args)
103+
results = ([len(exc.args), arg_count], [exc.args, args],
104+
[str(exc), str(args)],
105+
[repr(exc), exc.__class__.__name__ + repr(exc.args)])
106+
self.interface_test_driver(results)
107+
108+
def test_interface_no_arg(self):
109+
# Make sure that with no args that interface is correct
110+
exc = Exception()
111+
results = ([len(exc.args), 0], [exc.args, tuple()],
112+
[str(exc), ''],
113+
[repr(exc), exc.__class__.__name__ + '()'])
114+
self.interface_test_driver(results)
115+
116+
class UsageTests(unittest.TestCase):
117+
118+
"""Test usage of exceptions"""
119+
120+
def raise_fails(self, object_):
121+
"""Make sure that raising 'object_' triggers a TypeError."""
122+
try:
123+
raise object_
124+
except TypeError:
125+
return # What is expected.
126+
self.fail("TypeError expected for raising %s" % type(object_))
127+
128+
def catch_fails(self, object_):
129+
"""Catching 'object_' should raise a TypeError."""
130+
try:
131+
try:
132+
raise Exception
133+
except object_:
134+
pass
135+
except TypeError:
136+
pass
137+
except Exception:
138+
self.fail("TypeError expected when catching %s" % type(object_))
139+
140+
try:
141+
try:
142+
raise Exception
143+
except (object_,):
144+
pass
145+
except TypeError:
146+
return
147+
except Exception:
148+
self.fail("TypeError expected when catching %s as specified in a "
149+
"tuple" % type(object_))
150+
151+
def test_raise_new_style_non_exception(self):
152+
# You cannot raise a new-style class that does not inherit from
153+
# BaseException; the ability was not possible until BaseException's
154+
# introduction so no need to support new-style objects that do not
155+
# inherit from it.
156+
class NewStyleClass(object):
157+
pass
158+
self.raise_fails(NewStyleClass)
159+
self.raise_fails(NewStyleClass())
160+
161+
def test_raise_string(self):
162+
# Raising a string raises TypeError.
163+
self.raise_fails("spam")
164+
165+
def test_catch_non_BaseException(self):
166+
# Trying to catch an object that does not inherit from BaseException
167+
# is not allowed.
168+
class NonBaseException(object):
169+
pass
170+
self.catch_fails(NonBaseException)
171+
self.catch_fails(NonBaseException())
172+
173+
def test_catch_BaseException_instance(self):
174+
# Catching an instance of a BaseException subclass won't work.
175+
self.catch_fails(BaseException())
176+
177+
def test_catch_string(self):
178+
# Catching a string is bad.
179+
self.catch_fails("spam")
180+
181+
182+
if __name__ == '__main__':
183+
unittest.main()

0 commit comments

Comments
 (0)