python (3.12.0)

(root)/
lib/
python3.12/
test/
test_capi/
test_exceptions.py
       1  import errno
       2  import os
       3  import re
       4  import sys
       5  import unittest
       6  
       7  from test import support
       8  from test.support import import_helper
       9  from test.support.os_helper import TESTFN, TESTFN_UNDECODABLE
      10  from test.support.script_helper import assert_python_failure
      11  from test.support.testcase import ExceptionIsLikeMixin
      12  
      13  from .test_misc import decode_stderr
      14  
      15  # Skip this test if the _testcapi module isn't available.
      16  _testcapi = import_helper.import_module('_testcapi')
      17  
      18  NULL = None
      19  
      20  class ESC[4;38;5;81mTest_Exceptions(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
      21  
      22      def test_exception(self):
      23          raised_exception = ValueError("5")
      24          new_exc = TypeError("TEST")
      25          try:
      26              raise raised_exception
      27          except ValueError as e:
      28              orig_sys_exception = sys.exception()
      29              orig_exception = _testcapi.set_exception(new_exc)
      30              new_sys_exception = sys.exception()
      31              new_exception = _testcapi.set_exception(orig_exception)
      32              reset_sys_exception = sys.exception()
      33  
      34              self.assertEqual(orig_exception, e)
      35  
      36              self.assertEqual(orig_exception, raised_exception)
      37              self.assertEqual(orig_sys_exception, orig_exception)
      38              self.assertEqual(reset_sys_exception, orig_exception)
      39              self.assertEqual(new_exception, new_exc)
      40              self.assertEqual(new_sys_exception, new_exception)
      41          else:
      42              self.fail("Exception not raised")
      43  
      44      def test_exc_info(self):
      45          raised_exception = ValueError("5")
      46          new_exc = TypeError("TEST")
      47          try:
      48              raise raised_exception
      49          except ValueError as e:
      50              tb = e.__traceback__
      51              orig_sys_exc_info = sys.exc_info()
      52              orig_exc_info = _testcapi.set_exc_info(new_exc.__class__, new_exc, None)
      53              new_sys_exc_info = sys.exc_info()
      54              new_exc_info = _testcapi.set_exc_info(*orig_exc_info)
      55              reset_sys_exc_info = sys.exc_info()
      56  
      57              self.assertEqual(orig_exc_info[1], e)
      58  
      59              self.assertSequenceEqual(orig_exc_info, (raised_exception.__class__, raised_exception, tb))
      60              self.assertSequenceEqual(orig_sys_exc_info, orig_exc_info)
      61              self.assertSequenceEqual(reset_sys_exc_info, orig_exc_info)
      62              self.assertSequenceEqual(new_exc_info, (new_exc.__class__, new_exc, None))
      63              self.assertSequenceEqual(new_sys_exc_info, new_exc_info)
      64          else:
      65              self.assertTrue(False)
      66  
      67  
      68  class ESC[4;38;5;81mTest_FatalError(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
      69  
      70      def check_fatal_error(self, code, expected, not_expected=()):
      71          with support.SuppressCrashReport():
      72              rc, out, err = assert_python_failure('-sSI', '-c', code)
      73  
      74          err = decode_stderr(err)
      75          self.assertIn('Fatal Python error: _testcapi_fatal_error_impl: MESSAGE\n',
      76                        err)
      77  
      78          match = re.search(r'^Extension modules:(.*) \(total: ([0-9]+)\)$',
      79                            err, re.MULTILINE)
      80          if not match:
      81              self.fail(f"Cannot find 'Extension modules:' in {err!r}")
      82          modules = set(match.group(1).strip().split(', '))
      83          total = int(match.group(2))
      84  
      85          for name in expected:
      86              self.assertIn(name, modules)
      87          for name in not_expected:
      88              self.assertNotIn(name, modules)
      89          self.assertEqual(len(modules), total)
      90  
      91      @support.requires_subprocess()
      92      def test_fatal_error(self):
      93          # By default, stdlib extension modules are ignored,
      94          # but not test modules.
      95          expected = ('_testcapi',)
      96          not_expected = ('sys',)
      97          code = 'import _testcapi, sys; _testcapi.fatal_error(b"MESSAGE")'
      98          self.check_fatal_error(code, expected, not_expected)
      99  
     100          # Mark _testcapi as stdlib module, but not sys
     101          expected = ('sys',)
     102          not_expected = ('_testcapi',)
     103          code = """if True:
     104              import _testcapi, sys
     105              sys.stdlib_module_names = frozenset({"_testcapi"})
     106              _testcapi.fatal_error(b"MESSAGE")
     107          """
     108          self.check_fatal_error(code, expected)
     109  
     110  
     111  class ESC[4;38;5;81mTest_ErrSetAndRestore(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     112  
     113      def test_err_set_raised(self):
     114          with self.assertRaises(ValueError):
     115              _testcapi.err_set_raised(ValueError())
     116          v = ValueError()
     117          try:
     118              _testcapi.err_set_raised(v)
     119          except ValueError as ex:
     120              self.assertIs(v, ex)
     121  
     122      def test_err_restore(self):
     123          with self.assertRaises(ValueError):
     124              _testcapi.err_restore(ValueError)
     125          with self.assertRaises(ValueError):
     126              _testcapi.err_restore(ValueError, 1)
     127          with self.assertRaises(ValueError):
     128              _testcapi.err_restore(ValueError, 1, None)
     129          with self.assertRaises(ValueError):
     130              _testcapi.err_restore(ValueError, ValueError())
     131          try:
     132              _testcapi.err_restore(KeyError, "hi")
     133          except KeyError as k:
     134              self.assertEqual("hi", k.args[0])
     135          try:
     136              1/0
     137          except Exception as e:
     138              tb = e.__traceback__
     139          with self.assertRaises(ValueError):
     140              _testcapi.err_restore(ValueError, 1, tb)
     141          with self.assertRaises(TypeError):
     142              _testcapi.err_restore(ValueError, 1, 0)
     143          try:
     144              _testcapi.err_restore(ValueError, 1, tb)
     145          except ValueError as v:
     146              self.assertEqual(1, v.args[0])
     147              self.assertIs(tb, v.__traceback__.tb_next)
     148  
     149      def test_set_object(self):
     150  
     151          # new exception as obj is not an exception
     152          with self.assertRaises(ValueError) as e:
     153              _testcapi.exc_set_object(ValueError, 42)
     154          self.assertEqual(e.exception.args, (42,))
     155  
     156          # wraps the exception because unrelated types
     157          with self.assertRaises(ValueError) as e:
     158              _testcapi.exc_set_object(ValueError, TypeError(1,2,3))
     159          wrapped = e.exception.args[0]
     160          self.assertIsInstance(wrapped, TypeError)
     161          self.assertEqual(wrapped.args, (1, 2, 3))
     162  
     163          # is superclass, so does not wrap
     164          with self.assertRaises(PermissionError) as e:
     165              _testcapi.exc_set_object(OSError, PermissionError(24))
     166          self.assertEqual(e.exception.args, (24,))
     167  
     168          class ESC[4;38;5;81mMeta(ESC[4;38;5;149mtype):
     169              def __subclasscheck__(cls, sub):
     170                  1/0
     171  
     172          class ESC[4;38;5;81mBroken(ESC[4;38;5;149mException, metaclass=ESC[4;38;5;149mMeta):
     173              pass
     174  
     175          with self.assertRaises(ZeroDivisionError) as e:
     176              _testcapi.exc_set_object(Broken, Broken())
     177  
     178      def test_set_object_and_fetch(self):
     179          class ESC[4;38;5;81mBroken(ESC[4;38;5;149mException):
     180              def __init__(self, *arg):
     181                  raise ValueError("Broken __init__")
     182  
     183          exc = _testcapi.exc_set_object_fetch(Broken, 'abcd')
     184          self.assertIsInstance(exc, ValueError)
     185          self.assertEqual(exc.__notes__[0],
     186                           "Normalization failed: type=Broken args='abcd'")
     187  
     188          class ESC[4;38;5;81mBadArg:
     189              def __repr__(self):
     190                  raise TypeError('Broken arg type')
     191  
     192          exc = _testcapi.exc_set_object_fetch(Broken, BadArg())
     193          self.assertIsInstance(exc, ValueError)
     194          self.assertEqual(exc.__notes__[0],
     195                           'Normalization failed: type=Broken args=<unknown>')
     196  
     197      def test_set_string(self):
     198          """Test PyErr_SetString()"""
     199          setstring = _testcapi.err_setstring
     200          with self.assertRaises(ZeroDivisionError) as e:
     201              setstring(ZeroDivisionError, b'error')
     202          self.assertEqual(e.exception.args, ('error',))
     203          with self.assertRaises(ZeroDivisionError) as e:
     204              setstring(ZeroDivisionError, 'помилка'.encode())
     205          self.assertEqual(e.exception.args, ('помилка',))
     206  
     207          with self.assertRaises(UnicodeDecodeError):
     208              setstring(ZeroDivisionError, b'\xff')
     209          self.assertRaises(SystemError, setstring, list, b'error')
     210          # CRASHES setstring(ZeroDivisionError, NULL)
     211          # CRASHES setstring(NULL, b'error')
     212  
     213      def test_format(self):
     214          """Test PyErr_Format()"""
     215          import_helper.import_module('ctypes')
     216          from ctypes import pythonapi, py_object, c_char_p, c_int
     217          name = "PyErr_Format"
     218          PyErr_Format = getattr(pythonapi, name)
     219          PyErr_Format.argtypes = (py_object, c_char_p,)
     220          PyErr_Format.restype = py_object
     221          with self.assertRaises(ZeroDivisionError) as e:
     222              PyErr_Format(ZeroDivisionError, b'%s %d', b'error', c_int(42))
     223          self.assertEqual(e.exception.args, ('error 42',))
     224          with self.assertRaises(ZeroDivisionError) as e:
     225              PyErr_Format(ZeroDivisionError, b'%s', 'помилка'.encode())
     226          self.assertEqual(e.exception.args, ('помилка',))
     227  
     228          with self.assertRaisesRegex(OverflowError, 'not in range'):
     229              PyErr_Format(ZeroDivisionError, b'%c', c_int(-1))
     230          with self.assertRaisesRegex(ValueError, 'format string'):
     231              PyErr_Format(ZeroDivisionError, b'\xff')
     232          self.assertRaises(SystemError, PyErr_Format, list, b'error')
     233          # CRASHES PyErr_Format(ZeroDivisionError, NULL)
     234          # CRASHES PyErr_Format(py_object(), b'error')
     235  
     236      def test_setfromerrnowithfilename(self):
     237          """Test PyErr_SetFromErrnoWithFilename()"""
     238          setfromerrnowithfilename = _testcapi.err_setfromerrnowithfilename
     239          ENOENT = errno.ENOENT
     240          with self.assertRaises(FileNotFoundError) as e:
     241              setfromerrnowithfilename(ENOENT, OSError, b'file')
     242          self.assertEqual(e.exception.args,
     243                           (ENOENT, 'No such file or directory'))
     244          self.assertEqual(e.exception.errno, ENOENT)
     245          self.assertEqual(e.exception.filename, 'file')
     246  
     247          with self.assertRaises(FileNotFoundError) as e:
     248              setfromerrnowithfilename(ENOENT, OSError, os.fsencode(TESTFN))
     249          self.assertEqual(e.exception.filename, TESTFN)
     250  
     251          if TESTFN_UNDECODABLE:
     252              with self.assertRaises(FileNotFoundError) as e:
     253                  setfromerrnowithfilename(ENOENT, OSError, TESTFN_UNDECODABLE)
     254              self.assertEqual(e.exception.filename,
     255                               os.fsdecode(TESTFN_UNDECODABLE))
     256  
     257          with self.assertRaises(FileNotFoundError) as e:
     258              setfromerrnowithfilename(ENOENT, OSError, NULL)
     259          self.assertIsNone(e.exception.filename)
     260  
     261          with self.assertRaises(OSError) as e:
     262              setfromerrnowithfilename(0, OSError, b'file')
     263          self.assertEqual(e.exception.args, (0, 'Error'))
     264          self.assertEqual(e.exception.errno, 0)
     265          self.assertEqual(e.exception.filename, 'file')
     266  
     267          with self.assertRaises(ZeroDivisionError) as e:
     268              setfromerrnowithfilename(ENOENT, ZeroDivisionError, b'file')
     269          self.assertEqual(e.exception.args,
     270                           (ENOENT, 'No such file or directory', 'file'))
     271          # CRASHES setfromerrnowithfilename(ENOENT, NULL, b'error')
     272  
     273  
     274  class ESC[4;38;5;81mTest_PyUnstable_Exc_PrepReraiseStar(ESC[4;38;5;149mExceptionIsLikeMixin, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     275  
     276      def setUp(self):
     277          super().setUp()
     278          try:
     279              raise ExceptionGroup("eg", [TypeError('bad type'), ValueError(42)])
     280          except ExceptionGroup as e:
     281              self.orig = e
     282  
     283      def test_invalid_args(self):
     284          with self.assertRaisesRegex(TypeError, "orig must be an exception"):
     285              _testcapi.unstable_exc_prep_reraise_star(42, [None])
     286  
     287          with self.assertRaisesRegex(TypeError, "excs must be a list"):
     288              _testcapi.unstable_exc_prep_reraise_star(self.orig, 42)
     289  
     290          with self.assertRaisesRegex(TypeError, "not an exception"):
     291              _testcapi.unstable_exc_prep_reraise_star(self.orig, [TypeError(42), 42])
     292  
     293          with self.assertRaisesRegex(ValueError, "orig must be a raised exception"):
     294              _testcapi.unstable_exc_prep_reraise_star(ValueError(42), [TypeError(42)])
     295  
     296          with self.assertRaisesRegex(ValueError, "orig must be a raised exception"):
     297              _testcapi.unstable_exc_prep_reraise_star(ExceptionGroup("eg", [ValueError(42)]),
     298                                                       [TypeError(42)])
     299  
     300  
     301      def test_nothing_to_reraise(self):
     302          self.assertEqual(
     303              _testcapi.unstable_exc_prep_reraise_star(self.orig, [None]), None)
     304  
     305          try:
     306              raise ValueError(42)
     307          except ValueError as e:
     308              orig = e
     309          self.assertEqual(
     310              _testcapi.unstable_exc_prep_reraise_star(orig, [None]), None)
     311  
     312      def test_reraise_orig(self):
     313          orig = self.orig
     314          res = _testcapi.unstable_exc_prep_reraise_star(orig, [orig])
     315          self.assertExceptionIsLike(res, orig)
     316  
     317      def test_raise_orig_parts(self):
     318          orig = self.orig
     319          match, rest = orig.split(TypeError)
     320  
     321          test_cases = [
     322              ([match, rest], orig),
     323              ([rest, match], orig),
     324              ([match], match),
     325              ([rest], rest),
     326              ([], None),
     327          ]
     328  
     329          for input, expected in test_cases:
     330              with self.subTest(input=input):
     331                  res = _testcapi.unstable_exc_prep_reraise_star(orig, input)
     332                  self.assertExceptionIsLike(res, expected)
     333  
     334  
     335      def test_raise_with_new_exceptions(self):
     336          orig = self.orig
     337  
     338          match, rest = orig.split(TypeError)
     339          new1 = OSError('bad file')
     340          new2 = RuntimeError('bad runtime')
     341  
     342          test_cases = [
     343              ([new1, match, rest], ExceptionGroup("", [new1, orig])),
     344              ([match, new1, rest], ExceptionGroup("", [new1, orig])),
     345              ([match, rest, new1], ExceptionGroup("", [new1, orig])),
     346  
     347              ([new1, new2, match, rest], ExceptionGroup("", [new1, new2, orig])),
     348              ([new1, match, new2, rest], ExceptionGroup("", [new1, new2, orig])),
     349              ([new2, rest, match, new1], ExceptionGroup("", [new2, new1, orig])),
     350              ([rest, new2, match, new1], ExceptionGroup("", [new2, new1, orig])),
     351  
     352  
     353              ([new1, new2, rest], ExceptionGroup("", [new1, new2, rest])),
     354              ([new1, match, new2], ExceptionGroup("", [new1, new2, match])),
     355              ([rest, new2, new1], ExceptionGroup("", [new2, new1, rest])),
     356              ([new1, new2], ExceptionGroup("", [new1, new2])),
     357              ([new2, new1], ExceptionGroup("", [new2, new1])),
     358          ]
     359  
     360          for (input, expected) in test_cases:
     361              with self.subTest(input=input):
     362                  res = _testcapi.unstable_exc_prep_reraise_star(orig, input)
     363                  self.assertExceptionIsLike(res, expected)
     364  
     365  
     366  if __name__ == "__main__":
     367      unittest.main()