• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Python test set -- part 5, built-in exceptions
2
3import os
4import sys
5import unittest
6import pickle, cPickle
7
8from test.test_support import (TESTFN, unlink, run_unittest, captured_output,
9                               check_warnings, cpython_only)
10from test.test_pep352 import ignore_deprecation_warnings
11
12# XXX This is not really enough, each *operation* should be tested!
13
14class ExceptionTests(unittest.TestCase):
15
16    def testReload(self):
17        # Reloading the built-in exceptions module failed prior to Py2.2, while it
18        # should act the same as reloading built-in sys.
19        try:
20            from imp import reload
21            import exceptions
22            reload(exceptions)
23        except ImportError, e:
24            self.fail("reloading exceptions: %s" % e)
25
26    def raise_catch(self, exc, excname):
27        try:
28            raise exc, "spam"
29        except exc, err:
30            buf1 = str(err)
31        try:
32            raise exc("spam")
33        except exc, err:
34            buf2 = str(err)
35        self.assertEqual(buf1, buf2)
36        self.assertEqual(exc.__name__, excname)
37
38    def testRaising(self):
39        self.raise_catch(AttributeError, "AttributeError")
40        self.assertRaises(AttributeError, getattr, sys, "undefined_attribute")
41
42        self.raise_catch(EOFError, "EOFError")
43        fp = open(TESTFN, 'w')
44        fp.close()
45        fp = open(TESTFN, 'r')
46        savestdin = sys.stdin
47        try:
48            try:
49                sys.stdin = fp
50                x = raw_input()
51            except EOFError:
52                pass
53        finally:
54            sys.stdin = savestdin
55            fp.close()
56            unlink(TESTFN)
57
58        self.raise_catch(IOError, "IOError")
59        self.assertRaises(IOError, open, 'this file does not exist', 'r')
60
61        self.raise_catch(ImportError, "ImportError")
62        self.assertRaises(ImportError, __import__, "undefined_module")
63
64        self.raise_catch(IndexError, "IndexError")
65        x = []
66        self.assertRaises(IndexError, x.__getitem__, 10)
67
68        self.raise_catch(KeyError, "KeyError")
69        x = {}
70        self.assertRaises(KeyError, x.__getitem__, 'key')
71
72        self.raise_catch(KeyboardInterrupt, "KeyboardInterrupt")
73
74        self.raise_catch(MemoryError, "MemoryError")
75
76        self.raise_catch(NameError, "NameError")
77        try: x = undefined_variable
78        except NameError: pass
79
80        self.raise_catch(OverflowError, "OverflowError")
81        x = 1
82        for dummy in range(128):
83            x += x  # this simply shouldn't blow up
84
85        self.raise_catch(RuntimeError, "RuntimeError")
86
87        self.raise_catch(SyntaxError, "SyntaxError")
88        try: exec '/\n'
89        except SyntaxError: pass
90
91        self.raise_catch(IndentationError, "IndentationError")
92
93        self.raise_catch(TabError, "TabError")
94        # can only be tested under -tt, and is the only test for -tt
95        #try: compile("try:\n\t1/0\n    \t1/0\nfinally:\n pass\n", '<string>', 'exec')
96        #except TabError: pass
97        #else: self.fail("TabError not raised")
98
99        self.raise_catch(SystemError, "SystemError")
100
101        self.raise_catch(SystemExit, "SystemExit")
102        self.assertRaises(SystemExit, sys.exit, 0)
103
104        self.raise_catch(TypeError, "TypeError")
105        try: [] + ()
106        except TypeError: pass
107
108        self.raise_catch(ValueError, "ValueError")
109        self.assertRaises(ValueError, chr, 10000)
110
111        self.raise_catch(ZeroDivisionError, "ZeroDivisionError")
112        try: x = 1 // 0
113        except ZeroDivisionError: pass
114
115        self.raise_catch(Exception, "Exception")
116        try: x = 1 // 0
117        except Exception, e: pass
118
119    def testSyntaxErrorMessage(self):
120        # make sure the right exception message is raised for each of
121        # these code fragments
122
123        def ckmsg(src, msg):
124            try:
125                compile(src, '<fragment>', 'exec')
126            except SyntaxError, e:
127                if e.msg != msg:
128                    self.fail("expected %s, got %s" % (msg, e.msg))
129            else:
130                self.fail("failed to get expected SyntaxError")
131
132        s = '''while 1:
133            try:
134                pass
135            finally:
136                continue'''
137
138        if not sys.platform.startswith('java'):
139            ckmsg(s, "'continue' not supported inside 'finally' clause")
140
141        s = '''if 1:
142        try:
143            continue
144        except:
145            pass'''
146
147        ckmsg(s, "'continue' not properly in loop")
148        ckmsg("continue\n", "'continue' not properly in loop")
149
150    @cpython_only
151    def testSettingException(self):
152        # test that setting an exception at the C level works even if the
153        # exception object can't be constructed.
154
155        class BadException:
156            def __init__(self_):
157                raise RuntimeError, "can't instantiate BadException"
158
159        def test_capi1():
160            import _testcapi
161            try:
162                _testcapi.raise_exception(BadException, 1)
163            except TypeError, err:
164                exc, err, tb = sys.exc_info()
165                co = tb.tb_frame.f_code
166                self.assertEqual(co.co_name, "test_capi1")
167                self.assertTrue(co.co_filename.endswith('test_exceptions'+os.extsep+'py'))
168            else:
169                self.fail("Expected exception")
170
171        def test_capi2():
172            import _testcapi
173            try:
174                _testcapi.raise_exception(BadException, 0)
175            except RuntimeError, err:
176                exc, err, tb = sys.exc_info()
177                co = tb.tb_frame.f_code
178                self.assertEqual(co.co_name, "__init__")
179                self.assertTrue(co.co_filename.endswith('test_exceptions'+os.extsep+'py'))
180                co2 = tb.tb_frame.f_back.f_code
181                self.assertEqual(co2.co_name, "test_capi2")
182            else:
183                self.fail("Expected exception")
184
185        if not sys.platform.startswith('java'):
186            test_capi1()
187            test_capi2()
188
189    def test_WindowsError(self):
190        try:
191            WindowsError
192        except NameError:
193            pass
194        else:
195            self.assertEqual(str(WindowsError(1001)),
196                                 "1001")
197            self.assertEqual(str(WindowsError(1001, "message")),
198                                 "[Error 1001] message")
199            self.assertEqual(WindowsError(1001, "message").errno, 22)
200            self.assertEqual(WindowsError(1001, "message").winerror, 1001)
201
202    @ignore_deprecation_warnings
203    def testAttributes(self):
204        # test that exception attributes are happy
205
206        exceptionList = [
207            (BaseException, (), {'message' : '', 'args' : ()}),
208            (BaseException, (1, ), {'message' : 1, 'args' : (1,)}),
209            (BaseException, ('foo',),
210                {'message' : 'foo', 'args' : ('foo',)}),
211            (BaseException, ('foo', 1),
212                {'message' : '', 'args' : ('foo', 1)}),
213            (SystemExit, ('foo',),
214                {'message' : 'foo', 'args' : ('foo',), 'code' : 'foo'}),
215            (IOError, ('foo',),
216                {'message' : 'foo', 'args' : ('foo',), 'filename' : None,
217                 'errno' : None, 'strerror' : None}),
218            (IOError, ('foo', 'bar'),
219                {'message' : '', 'args' : ('foo', 'bar'), 'filename' : None,
220                 'errno' : 'foo', 'strerror' : 'bar'}),
221            (IOError, ('foo', 'bar', 'baz'),
222                {'message' : '', 'args' : ('foo', 'bar'), 'filename' : 'baz',
223                 'errno' : 'foo', 'strerror' : 'bar'}),
224            (IOError, ('foo', 'bar', 'baz', 'quux'),
225                {'message' : '', 'args' : ('foo', 'bar', 'baz', 'quux')}),
226            (EnvironmentError, ('errnoStr', 'strErrorStr', 'filenameStr'),
227                {'message' : '', 'args' : ('errnoStr', 'strErrorStr'),
228                 'strerror' : 'strErrorStr', 'errno' : 'errnoStr',
229                 'filename' : 'filenameStr'}),
230            (EnvironmentError, (1, 'strErrorStr', 'filenameStr'),
231                {'message' : '', 'args' : (1, 'strErrorStr'), 'errno' : 1,
232                 'strerror' : 'strErrorStr', 'filename' : 'filenameStr'}),
233            (SyntaxError, (), {'message' : '', 'msg' : None, 'text' : None,
234                'filename' : None, 'lineno' : None, 'offset' : None,
235                'print_file_and_line' : None}),
236            (SyntaxError, ('msgStr',),
237                {'message' : 'msgStr', 'args' : ('msgStr',), 'text' : None,
238                 'print_file_and_line' : None, 'msg' : 'msgStr',
239                 'filename' : None, 'lineno' : None, 'offset' : None}),
240            (SyntaxError, ('msgStr', ('filenameStr', 'linenoStr', 'offsetStr',
241                           'textStr')),
242                {'message' : '', 'offset' : 'offsetStr', 'text' : 'textStr',
243                 'args' : ('msgStr', ('filenameStr', 'linenoStr',
244                                      'offsetStr', 'textStr')),
245                 'print_file_and_line' : None, 'msg' : 'msgStr',
246                 'filename' : 'filenameStr', 'lineno' : 'linenoStr'}),
247            (SyntaxError, ('msgStr', 'filenameStr', 'linenoStr', 'offsetStr',
248                           'textStr', 'print_file_and_lineStr'),
249                {'message' : '', 'text' : None,
250                 'args' : ('msgStr', 'filenameStr', 'linenoStr', 'offsetStr',
251                           'textStr', 'print_file_and_lineStr'),
252                 'print_file_and_line' : None, 'msg' : 'msgStr',
253                 'filename' : None, 'lineno' : None, 'offset' : None}),
254            (UnicodeError, (), {'message' : '', 'args' : (),}),
255            (UnicodeEncodeError, ('ascii', u'a', 0, 1, 'ordinal not in range'),
256                {'message' : '', 'args' : ('ascii', u'a', 0, 1,
257                                           'ordinal not in range'),
258                 'encoding' : 'ascii', 'object' : u'a',
259                 'start' : 0, 'reason' : 'ordinal not in range'}),
260            (UnicodeDecodeError, ('ascii', '\xff', 0, 1, 'ordinal not in range'),
261                {'message' : '', 'args' : ('ascii', '\xff', 0, 1,
262                                           'ordinal not in range'),
263                 'encoding' : 'ascii', 'object' : '\xff',
264                 'start' : 0, 'reason' : 'ordinal not in range'}),
265            (UnicodeTranslateError, (u"\u3042", 0, 1, "ouch"),
266                {'message' : '', 'args' : (u'\u3042', 0, 1, 'ouch'),
267                 'object' : u'\u3042', 'reason' : 'ouch',
268                 'start' : 0, 'end' : 1}),
269        ]
270        try:
271            exceptionList.append(
272                (WindowsError, (1, 'strErrorStr', 'filenameStr'),
273                    {'message' : '', 'args' : (1, 'strErrorStr'),
274                     'strerror' : 'strErrorStr', 'winerror' : 1,
275                     'errno' : 22, 'filename' : 'filenameStr'})
276            )
277        except NameError:
278            pass
279
280        for exc, args, expected in exceptionList:
281            try:
282                raise exc(*args)
283            except BaseException, e:
284                if type(e) is not exc:
285                    raise
286                # Verify module name
287                self.assertEqual(type(e).__module__, 'exceptions')
288                # Verify no ref leaks in Exc_str()
289                s = str(e)
290                for checkArgName in expected:
291                    self.assertEqual(repr(getattr(e, checkArgName)),
292                                     repr(expected[checkArgName]),
293                                     'exception "%s", attribute "%s"' %
294                                      (repr(e), checkArgName))
295
296                # test for pickling support
297                for p in pickle, cPickle:
298                    for protocol in range(p.HIGHEST_PROTOCOL + 1):
299                        new = p.loads(p.dumps(e, protocol))
300                        for checkArgName in expected:
301                            got = repr(getattr(new, checkArgName))
302                            want = repr(expected[checkArgName])
303                            self.assertEqual(got, want,
304                                             'pickled "%r", attribute "%s"' %
305                                             (e, checkArgName))
306
307
308    def testDeprecatedMessageAttribute(self):
309        # Accessing BaseException.message and relying on its value set by
310        # BaseException.__init__ triggers a deprecation warning.
311        exc = BaseException("foo")
312        with check_warnings(("BaseException.message has been deprecated "
313                             "as of Python 2.6", DeprecationWarning)) as w:
314            self.assertEqual(exc.message, "foo")
315        self.assertEqual(len(w.warnings), 1)
316
317    def testRegularMessageAttribute(self):
318        # Accessing BaseException.message after explicitly setting a value
319        # for it does not trigger a deprecation warning.
320        exc = BaseException("foo")
321        exc.message = "bar"
322        with check_warnings(quiet=True) as w:
323            self.assertEqual(exc.message, "bar")
324        self.assertEqual(len(w.warnings), 0)
325        # Deleting the message is supported, too.
326        del exc.message
327        with self.assertRaises(AttributeError):
328            exc.message
329
330    @ignore_deprecation_warnings
331    def testPickleMessageAttribute(self):
332        # Pickling with message attribute must work, as well.
333        e = Exception("foo")
334        f = Exception("foo")
335        f.message = "bar"
336        for p in pickle, cPickle:
337            ep = p.loads(p.dumps(e))
338            self.assertEqual(ep.message, "foo")
339            fp = p.loads(p.dumps(f))
340            self.assertEqual(fp.message, "bar")
341
342    @ignore_deprecation_warnings
343    def testSlicing(self):
344        # Test that you can slice an exception directly instead of requiring
345        # going through the 'args' attribute.
346        args = (1, 2, 3)
347        exc = BaseException(*args)
348        self.assertEqual(exc[:], args)
349        self.assertEqual(exc.args[:], args)
350
351    def testKeywordArgs(self):
352        # test that builtin exception don't take keyword args,
353        # but user-defined subclasses can if they want
354        self.assertRaises(TypeError, BaseException, a=1)
355
356        class DerivedException(BaseException):
357            def __init__(self, fancy_arg):
358                BaseException.__init__(self)
359                self.fancy_arg = fancy_arg
360
361        x = DerivedException(fancy_arg=42)
362        self.assertEqual(x.fancy_arg, 42)
363
364    def testInfiniteRecursion(self):
365        def f():
366            return f()
367        self.assertRaises(RuntimeError, f)
368
369        def g():
370            try:
371                return g()
372            except ValueError:
373                return -1
374
375        # The test prints an unraisable recursion error when
376        # doing "except ValueError", this is because subclass
377        # checking has recursion checking too.
378        with captured_output("stderr"):
379            try:
380                g()
381            except RuntimeError:
382                pass
383            except:
384                self.fail("Should have raised KeyError")
385            else:
386                self.fail("Should have raised KeyError")
387
388    def testUnicodeStrUsage(self):
389        # Make sure both instances and classes have a str and unicode
390        # representation.
391        self.assertTrue(str(Exception))
392        self.assertTrue(unicode(Exception))
393        self.assertTrue(str(Exception('a')))
394        self.assertTrue(unicode(Exception(u'a')))
395        self.assertTrue(unicode(Exception(u'\xe1')))
396
397    def testUnicodeChangeAttributes(self):
398        # See issue 7309. This was a crasher.
399
400        u = UnicodeEncodeError('baz', u'xxxxx', 1, 5, 'foo')
401        self.assertEqual(str(u), "'baz' codec can't encode characters in position 1-4: foo")
402        u.end = 2
403        self.assertEqual(str(u), "'baz' codec can't encode character u'\\x78' in position 1: foo")
404        u.end = 5
405        u.reason = 0x345345345345345345
406        self.assertEqual(str(u), "'baz' codec can't encode characters in position 1-4: 965230951443685724997")
407        u.encoding = 4000
408        self.assertEqual(str(u), "'4000' codec can't encode characters in position 1-4: 965230951443685724997")
409        u.start = 1000
410        self.assertEqual(str(u), "'4000' codec can't encode characters in position 1000-4: 965230951443685724997")
411
412        u = UnicodeDecodeError('baz', 'xxxxx', 1, 5, 'foo')
413        self.assertEqual(str(u), "'baz' codec can't decode bytes in position 1-4: foo")
414        u.end = 2
415        self.assertEqual(str(u), "'baz' codec can't decode byte 0x78 in position 1: foo")
416        u.end = 5
417        u.reason = 0x345345345345345345
418        self.assertEqual(str(u), "'baz' codec can't decode bytes in position 1-4: 965230951443685724997")
419        u.encoding = 4000
420        self.assertEqual(str(u), "'4000' codec can't decode bytes in position 1-4: 965230951443685724997")
421        u.start = 1000
422        self.assertEqual(str(u), "'4000' codec can't decode bytes in position 1000-4: 965230951443685724997")
423
424        u = UnicodeTranslateError(u'xxxx', 1, 5, 'foo')
425        self.assertEqual(str(u), "can't translate characters in position 1-4: foo")
426        u.end = 2
427        self.assertEqual(str(u), "can't translate character u'\\x78' in position 1: foo")
428        u.end = 5
429        u.reason = 0x345345345345345345
430        self.assertEqual(str(u), "can't translate characters in position 1-4: 965230951443685724997")
431        u.start = 1000
432        self.assertEqual(str(u), "can't translate characters in position 1000-4: 965230951443685724997")
433
434    def test_badisinstance(self):
435        # Bug #2542: if issubclass(e, MyException) raises an exception,
436        # it should be ignored
437        class Meta(type):
438            def __subclasscheck__(cls, subclass):
439                raise ValueError()
440
441        class MyException(Exception):
442            __metaclass__ = Meta
443            pass
444
445        with captured_output("stderr") as stderr:
446            try:
447                raise KeyError()
448            except MyException, e:
449                self.fail("exception should not be a MyException")
450            except KeyError:
451                pass
452            except:
453                self.fail("Should have raised KeyError")
454            else:
455                self.fail("Should have raised KeyError")
456
457        with captured_output("stderr") as stderr:
458            def g():
459                try:
460                    return g()
461                except RuntimeError:
462                    return sys.exc_info()
463            e, v, tb = g()
464            self.assertTrue(e is RuntimeError, e)
465            self.assertIn("maximum recursion depth exceeded", str(v))
466
467
468
469# Helper class used by TestSameStrAndUnicodeMsg
470class ExcWithOverriddenStr(Exception):
471    """Subclass of Exception that accepts a keyword 'msg' arg that is
472    returned by __str__. 'msg' won't be included in self.args"""
473    def __init__(self, *args, **kwargs):
474        self.msg = kwargs.pop('msg') # msg should always be present
475        super(ExcWithOverriddenStr, self).__init__(*args, **kwargs)
476    def __str__(self):
477        return self.msg
478
479
480class TestSameStrAndUnicodeMsg(unittest.TestCase):
481    """unicode(err) should return the same message of str(err). See #6108"""
482
483    def check_same_msg(self, exc, msg):
484        """Helper function that checks if str(exc) == unicode(exc) == msg"""
485        self.assertEqual(str(exc), msg)
486        self.assertEqual(str(exc), unicode(exc))
487
488    def test_builtin_exceptions(self):
489        """Check same msg for built-in exceptions"""
490        # These exceptions implement a __str__ method that uses the args
491        # to create a better error message. unicode(e) should return the same
492        # message.
493        exceptions = [
494            SyntaxError('invalid syntax', ('<string>', 1, 3, '2+*3')),
495            IOError(2, 'No such file or directory'),
496            KeyError('both should have the same quotes'),
497            UnicodeDecodeError('ascii', '\xc3\xa0', 0, 1,
498                               'ordinal not in range(128)'),
499            UnicodeEncodeError('ascii', u'\u1234', 0, 1,
500                               'ordinal not in range(128)')
501        ]
502        for exception in exceptions:
503            self.assertEqual(str(exception), unicode(exception))
504
505    def test_0_args(self):
506        """Check same msg for Exception with 0 args"""
507        # str() and unicode() on an Exception with no args should return an
508        # empty string
509        self.check_same_msg(Exception(), '')
510
511    def test_0_args_with_overridden___str__(self):
512        """Check same msg for exceptions with 0 args and overridden __str__"""
513        # str() and unicode() on an exception with overridden __str__ that
514        # returns an ascii-only string should return the same string
515        for msg in ('foo', u'foo'):
516            self.check_same_msg(ExcWithOverriddenStr(msg=msg), msg)
517
518        # if __str__ returns a non-ascii unicode string str() should fail
519        # but unicode() should return the unicode string
520        e = ExcWithOverriddenStr(msg=u'f\xf6\xf6') # no args
521        self.assertRaises(UnicodeEncodeError, str, e)
522        self.assertEqual(unicode(e), u'f\xf6\xf6')
523
524    def test_1_arg(self):
525        """Check same msg for Exceptions with 1 arg"""
526        for arg in ('foo', u'foo'):
527            self.check_same_msg(Exception(arg), arg)
528
529        # if __str__ is not overridden and self.args[0] is a non-ascii unicode
530        # string, str() should try to return str(self.args[0]) and fail.
531        # unicode() should return unicode(self.args[0]) and succeed.
532        e = Exception(u'f\xf6\xf6')
533        self.assertRaises(UnicodeEncodeError, str, e)
534        self.assertEqual(unicode(e), u'f\xf6\xf6')
535
536    def test_1_arg_with_overridden___str__(self):
537        """Check same msg for exceptions with overridden __str__ and 1 arg"""
538        # when __str__ is overridden and __unicode__ is not implemented
539        # unicode(e) returns the same as unicode(e.__str__()).
540        for msg in ('foo', u'foo'):
541            self.check_same_msg(ExcWithOverriddenStr('arg', msg=msg), msg)
542
543        # if __str__ returns a non-ascii unicode string, str() should fail
544        # but unicode() should succeed.
545        e = ExcWithOverriddenStr('arg', msg=u'f\xf6\xf6') # 1 arg
546        self.assertRaises(UnicodeEncodeError, str, e)
547        self.assertEqual(unicode(e), u'f\xf6\xf6')
548
549    def test_many_args(self):
550        """Check same msg for Exceptions with many args"""
551        argslist = [
552            (3, 'foo'),
553            (1, u'foo', 'bar'),
554            (4, u'f\xf6\xf6', u'bar', 'baz')
555        ]
556        # both str() and unicode() should return a repr() of the args
557        for args in argslist:
558            self.check_same_msg(Exception(*args), repr(args))
559
560    def test_many_args_with_overridden___str__(self):
561        """Check same msg for exceptions with overridden __str__ and many args"""
562        # if __str__ returns an ascii string / ascii unicode string
563        # both str() and unicode() should succeed
564        for msg in ('foo', u'foo'):
565            e = ExcWithOverriddenStr('arg1', u'arg2', u'f\xf6\xf6', msg=msg)
566            self.check_same_msg(e, msg)
567
568        # if __str__ returns a non-ascii unicode string, str() should fail
569        # but unicode() should succeed
570        e = ExcWithOverriddenStr('arg1', u'f\xf6\xf6', u'arg3', # 3 args
571                                 msg=u'f\xf6\xf6')
572        self.assertRaises(UnicodeEncodeError, str, e)
573        self.assertEqual(unicode(e), u'f\xf6\xf6')
574
575    @cpython_only
576    def test_exception_with_doc(self):
577        import _testcapi
578        doc2 = "This is a test docstring."
579        doc4 = "This is another test docstring."
580
581        self.assertRaises(SystemError, _testcapi.make_exception_with_doc,
582                          "error1")
583
584        # test basic usage of PyErr_NewException
585        error1 = _testcapi.make_exception_with_doc("_testcapi.error1")
586        self.assertIs(type(error1), type)
587        self.assertTrue(issubclass(error1, Exception))
588        self.assertIsNone(error1.__doc__)
589
590        # test with given docstring
591        error2 = _testcapi.make_exception_with_doc("_testcapi.error2", doc2)
592        self.assertEqual(error2.__doc__, doc2)
593
594        # test with explicit base (without docstring)
595        error3 = _testcapi.make_exception_with_doc("_testcapi.error3",
596                                                   base=error2)
597        self.assertTrue(issubclass(error3, error2))
598
599        # test with explicit base tuple
600        class C(object):
601            pass
602        error4 = _testcapi.make_exception_with_doc("_testcapi.error4", doc4,
603                                                   (error3, C))
604        self.assertTrue(issubclass(error4, error3))
605        self.assertTrue(issubclass(error4, C))
606        self.assertEqual(error4.__doc__, doc4)
607
608        # test with explicit dictionary
609        error5 = _testcapi.make_exception_with_doc("_testcapi.error5", "",
610                                                   error4, {'a': 1})
611        self.assertTrue(issubclass(error5, error4))
612        self.assertEqual(error5.a, 1)
613        self.assertEqual(error5.__doc__, "")
614
615
616def test_main():
617    run_unittest(ExceptionTests, TestSameStrAndUnicodeMsg)
618
619if __name__ == '__main__':
620    test_main()
621