bpo-39019: Suppress only handled exception by "raise ... from ...".
This commit is contained in:
parent
a025d4ca99
commit
d5aacefb01
|
@ -469,9 +469,12 @@ class _IPAddressBase:
|
|||
return prefixlen
|
||||
|
||||
@classmethod
|
||||
def _report_invalid_netmask(cls, netmask_str):
|
||||
def _report_invalid_netmask(cls, netmask_str, suppress=True):
|
||||
msg = '%r is not a valid netmask' % netmask_str
|
||||
raise NetmaskValueError(msg) from None
|
||||
if suppress:
|
||||
raise NetmaskValueError(msg) from None
|
||||
else:
|
||||
raise NetmaskValueError(msg)
|
||||
|
||||
@classmethod
|
||||
def _prefix_from_prefix_string(cls, prefixlen_str):
|
||||
|
@ -489,13 +492,13 @@ class _IPAddressBase:
|
|||
# int allows a leading +/- as well as surrounding whitespace,
|
||||
# so we ensure that isn't the case
|
||||
if not (prefixlen_str.isascii() and prefixlen_str.isdigit()):
|
||||
cls._report_invalid_netmask(prefixlen_str)
|
||||
cls._report_invalid_netmask(prefixlen_str, suppress=False)
|
||||
try:
|
||||
prefixlen = int(prefixlen_str)
|
||||
except ValueError:
|
||||
cls._report_invalid_netmask(prefixlen_str)
|
||||
if not (0 <= prefixlen <= cls._max_prefixlen):
|
||||
cls._report_invalid_netmask(prefixlen_str)
|
||||
cls._report_invalid_netmask(prefixlen_str, suppress=False)
|
||||
return prefixlen
|
||||
|
||||
@classmethod
|
||||
|
@ -1160,19 +1163,24 @@ class _BaseV4:
|
|||
if isinstance(arg, int):
|
||||
prefixlen = arg
|
||||
if not (0 <= prefixlen <= cls._max_prefixlen):
|
||||
cls._report_invalid_netmask(prefixlen)
|
||||
cls._report_invalid_netmask(prefixlen, suppress=False)
|
||||
else:
|
||||
try:
|
||||
# Check for a netmask in prefix length form
|
||||
prefixlen = cls._prefix_from_prefix_string(arg)
|
||||
except NetmaskValueError:
|
||||
# Check for a netmask or hostmask in dotted-quad form.
|
||||
# This may raise NetmaskValueError.
|
||||
prefixlen = cls._prefix_from_ip_string(arg)
|
||||
prefixlen = cls._prefix_from_string(arg)
|
||||
netmask = IPv4Address(cls._ip_int_from_prefix(prefixlen))
|
||||
cls._netmask_cache[arg] = netmask, prefixlen
|
||||
return cls._netmask_cache[arg]
|
||||
|
||||
@classmethod
|
||||
def _prefix_from_string(cls, arg):
|
||||
try:
|
||||
# Check for a netmask in prefix length form
|
||||
return cls._prefix_from_prefix_string(arg)
|
||||
except NetmaskValueError:
|
||||
pass
|
||||
# Check for a netmask or hostmask in dotted-quad form.
|
||||
# This may raise NetmaskValueError.
|
||||
return cls._prefix_from_ip_string(arg)
|
||||
|
||||
@classmethod
|
||||
def _ip_int_from_string(cls, ip_str):
|
||||
"""Turn the given IP string into an integer for comparison.
|
||||
|
@ -1592,7 +1600,7 @@ class _BaseV6:
|
|||
if isinstance(arg, int):
|
||||
prefixlen = arg
|
||||
if not (0 <= prefixlen <= cls._max_prefixlen):
|
||||
cls._report_invalid_netmask(prefixlen)
|
||||
cls._report_invalid_netmask(prefixlen, suppress=False)
|
||||
else:
|
||||
prefixlen = cls._prefix_from_prefix_string(arg)
|
||||
netmask = IPv6Address(cls._ip_int_from_prefix(prefixlen))
|
||||
|
|
|
@ -1893,7 +1893,7 @@ class ExceptionContextTestCase(unittest.TestCase):
|
|||
cm = self.assertRaises(configparser.InterpolationMissingOptionError)
|
||||
with cm:
|
||||
parser.get('Paths', 'my_dir')
|
||||
self.assertIs(cm.exception.__suppress_context__, True)
|
||||
self.assertIsNone(cm.exception.__context__)
|
||||
|
||||
def test_get_extended_interpolation(self):
|
||||
parser = configparser.ConfigParser(
|
||||
|
@ -1907,7 +1907,7 @@ class ExceptionContextTestCase(unittest.TestCase):
|
|||
cm = self.assertRaises(configparser.InterpolationMissingOptionError)
|
||||
with cm:
|
||||
parser.get('Paths', 'my_dir')
|
||||
self.assertIs(cm.exception.__suppress_context__, True)
|
||||
self.assertIsNone(cm.exception.__context__)
|
||||
|
||||
def test_missing_options(self):
|
||||
parser = configparser.ConfigParser()
|
||||
|
@ -1917,19 +1917,19 @@ class ExceptionContextTestCase(unittest.TestCase):
|
|||
""")
|
||||
with self.assertRaises(configparser.NoSectionError) as cm:
|
||||
parser.options('test')
|
||||
self.assertIs(cm.exception.__suppress_context__, True)
|
||||
self.assertIsNone(cm.exception.__context__)
|
||||
|
||||
def test_missing_section(self):
|
||||
config = configparser.ConfigParser()
|
||||
with self.assertRaises(configparser.NoSectionError) as cm:
|
||||
config.set('Section1', 'an_int', '15')
|
||||
self.assertIs(cm.exception.__suppress_context__, True)
|
||||
self.assertIsNone(cm.exception.__context__)
|
||||
|
||||
def test_remove_option(self):
|
||||
config = configparser.ConfigParser()
|
||||
with self.assertRaises(configparser.NoSectionError) as cm:
|
||||
config.remove_option('Section1', 'an_int')
|
||||
self.assertIs(cm.exception.__suppress_context__, True)
|
||||
self.assertIsNone(cm.exception.__context__)
|
||||
|
||||
|
||||
class ConvertersTestCase(BasicTestCase, unittest.TestCase):
|
||||
|
|
|
@ -920,10 +920,10 @@ class TestBaseExitStack:
|
|||
|
||||
exc = err_ctx.exception
|
||||
self.assertIsInstance(exc, UniqueException)
|
||||
self.assertIsInstance(exc.__context__, UniqueRuntimeError)
|
||||
self.assertIsNone(exc.__context__.__context__)
|
||||
self.assertIsNone(exc.__context__.__cause__)
|
||||
self.assertIs(exc.__cause__, exc.__context__)
|
||||
self.assertIsInstance(exc.__cause__, UniqueRuntimeError)
|
||||
self.assertIsNone(exc.__cause__.__context__)
|
||||
self.assertIsNone(exc.__cause__.__cause__)
|
||||
self.assertIsNone(exc.__context__)
|
||||
|
||||
|
||||
class TestExitStack(TestBaseExitStack, unittest.TestCase):
|
||||
|
|
|
@ -52,7 +52,9 @@ class BaseTestCase(unittest.TestCase):
|
|||
yield exc
|
||||
# Ensure we produce clean tracebacks on failure
|
||||
if exc.exception.__context__ is not None:
|
||||
self.assertTrue(exc.exception.__suppress_context__)
|
||||
self.assertFalse(exc.exception.__suppress_context__)
|
||||
self.assertIsNone(exc.exception.__context__)
|
||||
self.assertIsNone(exc.exception.__cause__)
|
||||
|
||||
def assertAddressError(self, details, *args):
|
||||
"""Ensure a clean AddressValueError"""
|
||||
|
|
|
@ -995,12 +995,19 @@ class EnvironTests(mapping_tests.BasicTestMappingProtocol):
|
|||
with self.assertRaises(KeyError) as cm:
|
||||
os.environ[missing]
|
||||
self.assertIs(cm.exception.args[0], missing)
|
||||
self.assertTrue(cm.exception.__suppress_context__)
|
||||
self.assertFalse(cm.exception.__suppress_context__)
|
||||
self.assertIsNone(cm.exception.__context__)
|
||||
self.assertIsNone(cm.exception.__cause__)
|
||||
|
||||
with self.assertRaises(KeyError) as cm:
|
||||
del os.environ[missing]
|
||||
try:
|
||||
1/0
|
||||
except:
|
||||
with self.assertRaises(KeyError) as cm:
|
||||
del os.environ[missing]
|
||||
self.assertIs(cm.exception.args[0], missing)
|
||||
self.assertTrue(cm.exception.__suppress_context__)
|
||||
self.assertFalse(cm.exception.__suppress_context__)
|
||||
self.assertIsInstance(cm.exception.__context__, ZeroDivisionError)
|
||||
self.assertIsNone(cm.exception.__cause__)
|
||||
|
||||
def _test_environ_iteration(self, collection):
|
||||
iterator = iter(collection)
|
||||
|
|
|
@ -77,14 +77,24 @@ class TestRaise(unittest.TestCase):
|
|||
nested_reraise()
|
||||
self.assertRaises(TypeError, reraise)
|
||||
|
||||
def test_raise_from_None(self):
|
||||
def test_raise_from_None1(self):
|
||||
try:
|
||||
raise TypeError("foo")
|
||||
except:
|
||||
try:
|
||||
raise ValueError() from None
|
||||
except ValueError as e:
|
||||
self.assertIsInstance(e.__context__, TypeError)
|
||||
self.assertIsNone(e.__cause__)
|
||||
|
||||
def test_raise_from_None2(self):
|
||||
try:
|
||||
try:
|
||||
raise TypeError("foo")
|
||||
except:
|
||||
raise ValueError() from None
|
||||
except ValueError as e:
|
||||
self.assertIsInstance(e.__context__, TypeError)
|
||||
self.assertIsNone(e.__context__)
|
||||
self.assertIsNone(e.__cause__)
|
||||
|
||||
def test_with_reraise1(self):
|
||||
|
@ -150,7 +160,27 @@ class TestRaise(unittest.TestCase):
|
|||
|
||||
class TestCause(unittest.TestCase):
|
||||
|
||||
def testCauseSyntax(self):
|
||||
def testCauseSyntax1(self):
|
||||
try:
|
||||
try:
|
||||
raise TypeError
|
||||
except Exception:
|
||||
try:
|
||||
raise ValueError from None
|
||||
except ValueError as exc:
|
||||
self.assertIsNone(exc.__cause__)
|
||||
self.assertIsInstance(exc.__context__, TypeError)
|
||||
self.assertTrue(exc.__suppress_context__)
|
||||
exc.__suppress_context__ = False
|
||||
raise exc
|
||||
except ValueError as exc:
|
||||
e = exc
|
||||
|
||||
self.assertIsNone(e.__cause__)
|
||||
self.assertFalse(e.__suppress_context__)
|
||||
self.assertIsInstance(e.__context__, TypeError)
|
||||
|
||||
def testCauseSyntax2(self):
|
||||
try:
|
||||
try:
|
||||
try:
|
||||
|
@ -159,16 +189,106 @@ class TestCause(unittest.TestCase):
|
|||
raise ValueError from None
|
||||
except ValueError as exc:
|
||||
self.assertIsNone(exc.__cause__)
|
||||
self.assertTrue(exc.__suppress_context__)
|
||||
exc.__suppress_context__ = False
|
||||
self.assertIsNone(exc.__context__)
|
||||
self.assertFalse(exc.__suppress_context__)
|
||||
raise exc
|
||||
except ValueError as exc:
|
||||
e = exc
|
||||
|
||||
self.assertIsNone(e.__cause__)
|
||||
self.assertFalse(e.__suppress_context__)
|
||||
self.assertIsNone(e.__context__)
|
||||
|
||||
def testCauseSyntax3(self):
|
||||
try:
|
||||
try:
|
||||
1/0
|
||||
except:
|
||||
try:
|
||||
raise TypeError
|
||||
except Exception:
|
||||
try:
|
||||
raise ValueError from None
|
||||
except ValueError as exc:
|
||||
self.assertIsNone(exc.__cause__)
|
||||
self.assertIsInstance(exc.__context__, TypeError)
|
||||
self.assertTrue(exc.__suppress_context__)
|
||||
exc.__suppress_context__ = False
|
||||
raise exc
|
||||
except ValueError as exc:
|
||||
e = exc
|
||||
|
||||
self.assertIsNone(e.__cause__)
|
||||
self.assertFalse(e.__suppress_context__)
|
||||
self.assertIsInstance(e.__context__, TypeError)
|
||||
|
||||
def testCauseSyntax4(self):
|
||||
try:
|
||||
try:
|
||||
try:
|
||||
1/0
|
||||
except:
|
||||
try:
|
||||
raise TypeError
|
||||
except Exception:
|
||||
raise ValueError from None
|
||||
except ValueError as exc:
|
||||
self.assertIsNone(exc.__cause__)
|
||||
self.assertIsInstance(exc.__context__, ZeroDivisionError)
|
||||
self.assertFalse(exc.__suppress_context__)
|
||||
raise exc
|
||||
except ValueError as exc:
|
||||
e = exc
|
||||
|
||||
self.assertIsNone(e.__cause__)
|
||||
self.assertFalse(e.__suppress_context__)
|
||||
self.assertIsInstance(e.__context__, ZeroDivisionError)
|
||||
|
||||
def testCauseSyntax5(self):
|
||||
try:
|
||||
try:
|
||||
1/0
|
||||
except:
|
||||
try:
|
||||
raise TypeError
|
||||
except Exception:
|
||||
try:
|
||||
raise ValueError from None
|
||||
except ValueError as exc:
|
||||
self.assertIsNone(exc.__cause__)
|
||||
self.assertIsInstance(exc.__context__, TypeError)
|
||||
self.assertTrue(exc.__suppress_context__)
|
||||
raise exc
|
||||
except ValueError as exc:
|
||||
e = exc
|
||||
|
||||
self.assertIsNone(e.__cause__)
|
||||
self.assertFalse(e.__suppress_context__)
|
||||
self.assertIsInstance(e.__context__, ZeroDivisionError)
|
||||
|
||||
def testCauseSyntax6(self):
|
||||
try:
|
||||
try:
|
||||
1/0
|
||||
except:
|
||||
try:
|
||||
try:
|
||||
raise TypeError
|
||||
except Exception:
|
||||
raise ValueError from None
|
||||
except ValueError as exc:
|
||||
self.assertIsNone(exc.__cause__)
|
||||
self.assertIsInstance(exc.__context__, ZeroDivisionError)
|
||||
self.assertFalse(exc.__suppress_context__)
|
||||
exc.__suppress_context__ = True
|
||||
raise exc
|
||||
except ValueError as exc:
|
||||
e = exc
|
||||
|
||||
self.assertIsNone(e.__cause__)
|
||||
self.assertFalse(e.__suppress_context__)
|
||||
self.assertIsNone(e.__context__)
|
||||
|
||||
def test_invalid_cause(self):
|
||||
try:
|
||||
raise IndexError from 5
|
||||
|
|
|
@ -243,13 +243,23 @@ class StrptimeTests(unittest.TestCase):
|
|||
|
||||
def test_strptime_exception_context(self):
|
||||
# check that this doesn't chain exceptions needlessly (see #17572)
|
||||
with self.assertRaises(ValueError) as e:
|
||||
_strptime._strptime_time('', '%D')
|
||||
self.assertIs(e.exception.__suppress_context__, True)
|
||||
try:
|
||||
1/0
|
||||
except:
|
||||
with self.assertRaises(ValueError) as e:
|
||||
_strptime._strptime_time('', '%D')
|
||||
self.assertFalse(e.exception.__suppress_context__)
|
||||
self.assertIsInstance(e.exception.__context__, ZeroDivisionError)
|
||||
self.assertIsNone(e.exception.__cause__)
|
||||
# additional check for IndexError branch (issue #19545)
|
||||
with self.assertRaises(ValueError) as e:
|
||||
_strptime._strptime_time('19', '%Y %')
|
||||
self.assertIs(e.exception.__suppress_context__, True)
|
||||
try:
|
||||
1/0
|
||||
except:
|
||||
with self.assertRaises(ValueError) as e:
|
||||
_strptime._strptime_time('19', '%Y %')
|
||||
self.assertFalse(e.exception.__suppress_context__)
|
||||
self.assertIsInstance(e.exception.__context__, ZeroDivisionError)
|
||||
self.assertIsNone(e.exception.__cause__)
|
||||
|
||||
def test_unconverteddata(self):
|
||||
# Check ValueError is raised when there is unconverted data
|
||||
|
|
|
@ -276,11 +276,15 @@ class TimeTestCase(unittest.TestCase):
|
|||
# check that this doesn't chain exceptions needlessly (see #17572)
|
||||
with self.assertRaises(ValueError) as e:
|
||||
time.strptime('', '%D')
|
||||
self.assertIs(e.exception.__suppress_context__, True)
|
||||
self.assertFalse(e.exception.__suppress_context__)
|
||||
self.assertIsNone(e.exception.__context__)
|
||||
self.assertIsNone(e.exception.__cause__)
|
||||
# additional check for IndexError branch (issue #19545)
|
||||
with self.assertRaises(ValueError) as e:
|
||||
time.strptime('19', '%Y %')
|
||||
self.assertIs(e.exception.__suppress_context__, True)
|
||||
self.assertFalse(e.exception.__suppress_context__)
|
||||
self.assertIsNone(e.exception.__context__)
|
||||
self.assertIsNone(e.exception.__cause__)
|
||||
|
||||
def test_asctime(self):
|
||||
time.asctime(time.gmtime(self.t))
|
||||
|
|
|
@ -1042,13 +1042,11 @@ class TestTracebackException(unittest.TestCase):
|
|||
self.assertEqual(exc_info[0], exc.exc_type)
|
||||
self.assertEqual(str(exc_info[1]), str(exc))
|
||||
|
||||
def test_cause(self):
|
||||
def test_cause1(self):
|
||||
try:
|
||||
try:
|
||||
1/0
|
||||
finally:
|
||||
exc_info_context = sys.exc_info()
|
||||
exc_context = traceback.TracebackException(*exc_info_context)
|
||||
cause = Exception("cause")
|
||||
raise Exception("uh oh") from cause
|
||||
except Exception:
|
||||
|
@ -1057,12 +1055,37 @@ class TestTracebackException(unittest.TestCase):
|
|||
expected_stack = traceback.StackSummary.extract(
|
||||
traceback.walk_tb(exc_info[2]))
|
||||
exc_cause = traceback.TracebackException(Exception, cause, None)
|
||||
self.assertEqual(exc_cause, exc.__cause__)
|
||||
self.assertEqual(exc_context, exc.__context__)
|
||||
self.assertEqual(True, exc.__suppress_context__)
|
||||
self.assertEqual(expected_stack, exc.stack)
|
||||
self.assertEqual(exc_info[0], exc.exc_type)
|
||||
self.assertEqual(str(exc_info[1]), str(exc))
|
||||
self.assertEqual(exc.__cause__, exc_cause)
|
||||
self.assertIsNone(exc.__context__)
|
||||
self.assertFalse(exc.__suppress_context__)
|
||||
self.assertEqual(exc.stack, expected_stack)
|
||||
self.assertEqual(exc.exc_type, exc_info[0])
|
||||
self.assertEqual(str(exc), str(exc_info[1]))
|
||||
|
||||
def test_cause2(self):
|
||||
try:
|
||||
try:
|
||||
raise TypeError
|
||||
except:
|
||||
exc_info_context = sys.exc_info()
|
||||
exc_context = traceback.TracebackException(*exc_info_context)
|
||||
try:
|
||||
1/0
|
||||
finally:
|
||||
cause = Exception("cause")
|
||||
raise Exception("uh oh") from cause
|
||||
except Exception:
|
||||
exc_info = sys.exc_info()
|
||||
exc = traceback.TracebackException(*exc_info)
|
||||
expected_stack = traceback.StackSummary.extract(
|
||||
traceback.walk_tb(exc_info[2]))
|
||||
exc_cause = traceback.TracebackException(Exception, cause, None)
|
||||
self.assertEqual(exc.__cause__, exc_cause)
|
||||
self.assertEqual(exc.__context__, exc_context)
|
||||
self.assertFalse(exc.__suppress_context__)
|
||||
self.assertEqual(exc.stack, expected_stack)
|
||||
self.assertEqual(exc.exc_type, exc_info[0])
|
||||
self.assertEqual(str(exc), str(exc_info[1]))
|
||||
|
||||
def test_context(self):
|
||||
try:
|
||||
|
|
|
@ -3608,6 +3608,23 @@ exception_unwind:
|
|||
|
||||
if (b->b_type == EXCEPT_HANDLER) {
|
||||
UNWIND_EXCEPT_HANDLER(b);
|
||||
if (tstate->curexc_value != NULL) {
|
||||
assert(PyExceptionInstance_Check(tstate->curexc_value));
|
||||
PyBaseExceptionObject *curexc_value = (PyBaseExceptionObject *)tstate->curexc_value;
|
||||
if (curexc_value->suppress_context) {
|
||||
PyObject *exc_value = _PyErr_GetTopmostException(tstate)->exc_value;
|
||||
if (exc_value == Py_None) {
|
||||
exc_value = NULL;
|
||||
}
|
||||
if ((PyObject *)curexc_value != exc_value &&
|
||||
curexc_value->context != exc_value)
|
||||
{
|
||||
curexc_value->suppress_context = 0;
|
||||
Py_XINCREF(exc_value);
|
||||
PyException_SetContext((PyObject *)curexc_value, exc_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
UNWIND_BLOCK(b);
|
||||
|
|
Loading…
Reference in New Issue