1 | import unittest
|
---|
2 | import __builtin__
|
---|
3 | import exceptions
|
---|
4 | import warnings
|
---|
5 | from test.test_support import run_unittest, check_warnings
|
---|
6 | import os
|
---|
7 | import sys
|
---|
8 | from platform import system as platform_system
|
---|
9 |
|
---|
10 | DEPRECATION_WARNINGS = ["BaseException.message has been deprecated"]
|
---|
11 |
|
---|
12 | if sys.py3kwarning:
|
---|
13 | DEPRECATION_WARNINGS.extend(
|
---|
14 | ["exceptions must derive from BaseException",
|
---|
15 | "catching classes that don't inherit from BaseException is not allowed",
|
---|
16 | "__get(item|slice)__ not supported for exception classes"])
|
---|
17 |
|
---|
18 | _deprecations = [(msg, DeprecationWarning) for msg in DEPRECATION_WARNINGS]
|
---|
19 |
|
---|
20 | # Silence Py3k and other deprecation warnings
|
---|
21 | def ignore_deprecation_warnings(func):
|
---|
22 | """Ignore the known DeprecationWarnings."""
|
---|
23 | def wrapper(*args, **kw):
|
---|
24 | with check_warnings(*_deprecations, quiet=True):
|
---|
25 | return func(*args, **kw)
|
---|
26 | return wrapper
|
---|
27 |
|
---|
28 | class ExceptionClassTests(unittest.TestCase):
|
---|
29 |
|
---|
30 | """Tests for anything relating to exception objects themselves (e.g.,
|
---|
31 | inheritance hierarchy)"""
|
---|
32 |
|
---|
33 | def test_builtins_new_style(self):
|
---|
34 | self.assertTrue(issubclass(Exception, object))
|
---|
35 |
|
---|
36 | @ignore_deprecation_warnings
|
---|
37 | def verify_instance_interface(self, ins):
|
---|
38 | for attr in ("args", "message", "__str__", "__repr__", "__getitem__"):
|
---|
39 | self.assertTrue(hasattr(ins, attr),
|
---|
40 | "%s missing %s attribute" %
|
---|
41 | (ins.__class__.__name__, attr))
|
---|
42 |
|
---|
43 | def test_inheritance(self):
|
---|
44 | # Make sure the inheritance hierarchy matches the documentation
|
---|
45 | exc_set = set(x for x in dir(exceptions) if not x.startswith('_'))
|
---|
46 | inheritance_tree = open(os.path.join(os.path.split(__file__)[0],
|
---|
47 | 'exception_hierarchy.txt'))
|
---|
48 | try:
|
---|
49 | superclass_name = inheritance_tree.readline().rstrip()
|
---|
50 | try:
|
---|
51 | last_exc = getattr(__builtin__, superclass_name)
|
---|
52 | except AttributeError:
|
---|
53 | self.fail("base class %s not a built-in" % superclass_name)
|
---|
54 | self.assertIn(superclass_name, exc_set)
|
---|
55 | exc_set.discard(superclass_name)
|
---|
56 | superclasses = [] # Loop will insert base exception
|
---|
57 | last_depth = 0
|
---|
58 | for exc_line in inheritance_tree:
|
---|
59 | exc_line = exc_line.rstrip()
|
---|
60 | depth = exc_line.rindex('-')
|
---|
61 | exc_name = exc_line[depth+2:] # Slice past space
|
---|
62 | if '(' in exc_name:
|
---|
63 | paren_index = exc_name.index('(')
|
---|
64 | platform_name = exc_name[paren_index+1:-1]
|
---|
65 | exc_name = exc_name[:paren_index-1] # Slice off space
|
---|
66 | if platform_system() != platform_name:
|
---|
67 | exc_set.discard(exc_name)
|
---|
68 | continue
|
---|
69 | if '[' in exc_name:
|
---|
70 | left_bracket = exc_name.index('[')
|
---|
71 | exc_name = exc_name[:left_bracket-1] # cover space
|
---|
72 | try:
|
---|
73 | exc = getattr(__builtin__, exc_name)
|
---|
74 | except AttributeError:
|
---|
75 | self.fail("%s not a built-in exception" % exc_name)
|
---|
76 | if last_depth < depth:
|
---|
77 | superclasses.append((last_depth, last_exc))
|
---|
78 | elif last_depth > depth:
|
---|
79 | while superclasses[-1][0] >= depth:
|
---|
80 | superclasses.pop()
|
---|
81 | self.assertTrue(issubclass(exc, superclasses[-1][1]),
|
---|
82 | "%s is not a subclass of %s" % (exc.__name__,
|
---|
83 | superclasses[-1][1].__name__))
|
---|
84 | try: # Some exceptions require arguments; just skip them
|
---|
85 | self.verify_instance_interface(exc())
|
---|
86 | except TypeError:
|
---|
87 | pass
|
---|
88 | self.assertIn(exc_name, exc_set)
|
---|
89 | exc_set.discard(exc_name)
|
---|
90 | last_exc = exc
|
---|
91 | last_depth = depth
|
---|
92 | finally:
|
---|
93 | inheritance_tree.close()
|
---|
94 | self.assertEqual(len(exc_set), 0, "%s not accounted for" % exc_set)
|
---|
95 |
|
---|
96 | interface_tests = ("length", "args", "message", "str", "unicode", "repr",
|
---|
97 | "indexing")
|
---|
98 |
|
---|
99 | def interface_test_driver(self, results):
|
---|
100 | for test_name, (given, expected) in zip(self.interface_tests, results):
|
---|
101 | self.assertEqual(given, expected, "%s: %s != %s" % (test_name,
|
---|
102 | given, expected))
|
---|
103 |
|
---|
104 | @ignore_deprecation_warnings
|
---|
105 | def test_interface_single_arg(self):
|
---|
106 | # Make sure interface works properly when given a single argument
|
---|
107 | arg = "spam"
|
---|
108 | exc = Exception(arg)
|
---|
109 | results = ([len(exc.args), 1], [exc.args[0], arg], [exc.message, arg],
|
---|
110 | [str(exc), str(arg)], [unicode(exc), unicode(arg)],
|
---|
111 | [repr(exc), exc.__class__.__name__ + repr(exc.args)],
|
---|
112 | [exc[0], arg])
|
---|
113 | self.interface_test_driver(results)
|
---|
114 |
|
---|
115 | @ignore_deprecation_warnings
|
---|
116 | def test_interface_multi_arg(self):
|
---|
117 | # Make sure interface correct when multiple arguments given
|
---|
118 | arg_count = 3
|
---|
119 | args = tuple(range(arg_count))
|
---|
120 | exc = Exception(*args)
|
---|
121 | results = ([len(exc.args), arg_count], [exc.args, args],
|
---|
122 | [exc.message, ''], [str(exc), str(args)],
|
---|
123 | [unicode(exc), unicode(args)],
|
---|
124 | [repr(exc), exc.__class__.__name__ + repr(exc.args)],
|
---|
125 | [exc[-1], args[-1]])
|
---|
126 | self.interface_test_driver(results)
|
---|
127 |
|
---|
128 | @ignore_deprecation_warnings
|
---|
129 | def test_interface_no_arg(self):
|
---|
130 | # Make sure that with no args that interface is correct
|
---|
131 | exc = Exception()
|
---|
132 | results = ([len(exc.args), 0], [exc.args, tuple()],
|
---|
133 | [exc.message, ''],
|
---|
134 | [str(exc), ''], [unicode(exc), u''],
|
---|
135 | [repr(exc), exc.__class__.__name__ + '()'], [True, True])
|
---|
136 | self.interface_test_driver(results)
|
---|
137 |
|
---|
138 |
|
---|
139 | def test_message_deprecation(self):
|
---|
140 | # As of Python 2.6, BaseException.message is deprecated.
|
---|
141 | with check_warnings(("", DeprecationWarning)):
|
---|
142 | BaseException().message
|
---|
143 |
|
---|
144 |
|
---|
145 | class UsageTests(unittest.TestCase):
|
---|
146 |
|
---|
147 | """Test usage of exceptions"""
|
---|
148 |
|
---|
149 | def raise_fails(self, object_):
|
---|
150 | """Make sure that raising 'object_' triggers a TypeError."""
|
---|
151 | try:
|
---|
152 | raise object_
|
---|
153 | except TypeError:
|
---|
154 | return # What is expected.
|
---|
155 | self.fail("TypeError expected for raising %s" % type(object_))
|
---|
156 |
|
---|
157 | def catch_fails(self, object_):
|
---|
158 | """Catching 'object_' should raise a TypeError."""
|
---|
159 | try:
|
---|
160 | try:
|
---|
161 | raise StandardError
|
---|
162 | except object_:
|
---|
163 | pass
|
---|
164 | except TypeError:
|
---|
165 | pass
|
---|
166 | except StandardError:
|
---|
167 | self.fail("TypeError expected when catching %s" % type(object_))
|
---|
168 |
|
---|
169 | try:
|
---|
170 | try:
|
---|
171 | raise StandardError
|
---|
172 | except (object_,):
|
---|
173 | pass
|
---|
174 | except TypeError:
|
---|
175 | return
|
---|
176 | except StandardError:
|
---|
177 | self.fail("TypeError expected when catching %s as specified in a "
|
---|
178 | "tuple" % type(object_))
|
---|
179 |
|
---|
180 | @ignore_deprecation_warnings
|
---|
181 | def test_raise_classic(self):
|
---|
182 | # Raising a classic class is okay (for now).
|
---|
183 | class ClassicClass:
|
---|
184 | pass
|
---|
185 | try:
|
---|
186 | raise ClassicClass
|
---|
187 | except ClassicClass:
|
---|
188 | pass
|
---|
189 | except:
|
---|
190 | self.fail("unable to raise classic class")
|
---|
191 | try:
|
---|
192 | raise ClassicClass()
|
---|
193 | except ClassicClass:
|
---|
194 | pass
|
---|
195 | except:
|
---|
196 | self.fail("unable to raise classic class instance")
|
---|
197 |
|
---|
198 | def test_raise_new_style_non_exception(self):
|
---|
199 | # You cannot raise a new-style class that does not inherit from
|
---|
200 | # BaseException; the ability was not possible until BaseException's
|
---|
201 | # introduction so no need to support new-style objects that do not
|
---|
202 | # inherit from it.
|
---|
203 | class NewStyleClass(object):
|
---|
204 | pass
|
---|
205 | self.raise_fails(NewStyleClass)
|
---|
206 | self.raise_fails(NewStyleClass())
|
---|
207 |
|
---|
208 | def test_raise_string(self):
|
---|
209 | # Raising a string raises TypeError.
|
---|
210 | self.raise_fails("spam")
|
---|
211 |
|
---|
212 | def test_catch_string(self):
|
---|
213 | # Catching a string should trigger a DeprecationWarning.
|
---|
214 | with warnings.catch_warnings():
|
---|
215 | warnings.resetwarnings()
|
---|
216 | warnings.filterwarnings("error")
|
---|
217 | str_exc = "spam"
|
---|
218 | with self.assertRaises(DeprecationWarning):
|
---|
219 | try:
|
---|
220 | raise StandardError
|
---|
221 | except str_exc:
|
---|
222 | pass
|
---|
223 |
|
---|
224 | # Make sure that even if the string exception is listed in a tuple
|
---|
225 | # that a warning is raised.
|
---|
226 | with self.assertRaises(DeprecationWarning):
|
---|
227 | try:
|
---|
228 | raise StandardError
|
---|
229 | except (AssertionError, str_exc):
|
---|
230 | pass
|
---|
231 |
|
---|
232 |
|
---|
233 | def test_main():
|
---|
234 | run_unittest(ExceptionClassTests, UsageTests)
|
---|
235 |
|
---|
236 |
|
---|
237 |
|
---|
238 | if __name__ == '__main__':
|
---|
239 | test_main()
|
---|