Home | History | Annotate | Download | only in test
      1 # Python test set -- part 5, built-in exceptions

      2 
      3 import os
      4 import sys
      5 import unittest
      6 import pickle, cPickle
      7 
      8 from test.test_support import (TESTFN, unlink, run_unittest, captured_output,
      9                                check_warnings, cpython_only)
     10 from test.test_pep352 import ignore_deprecation_warnings
     11 
     12 # XXX This is not really enough, each *operation* should be tested!

     13 
     14 class 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

    470 class 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 
    480 class 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 
    616 def test_main():
    617     run_unittest(ExceptionTests, TestSameStrAndUnicodeMsg)
    618 
    619 if __name__ == '__main__':
    620     test_main()
    621