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