Skip to content

Commit 2738a16

Browse files
authored
Merge pull request RustPython#1757 from palaviv/test-baseexception
Add more CPython tests
2 parents c947ec0 + 29cf716 commit 2738a16

7 files changed

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

0 commit comments

Comments
 (0)