mirror of
https://github.com/python/cpython.git
synced 2026-01-26 21:03:34 +00:00
[3.14] gh-143377: fix crashes in _interpreters.capture_exception (GH-143418) (#143652)
gh-143377: fix crashes in `_interpreters.capture_exception` (GH-143418) (cherry picked from commit ce6bae92da671e31013b00901591ce2b595b61ce) Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
This commit is contained in:
parent
5728089b73
commit
ea70d16406
@ -3,10 +3,12 @@ import importlib
|
||||
import importlib.util
|
||||
import itertools
|
||||
import sys
|
||||
import traceback
|
||||
import types
|
||||
import unittest
|
||||
import warnings
|
||||
|
||||
from test import support
|
||||
from test.support import import_helper
|
||||
|
||||
_testinternalcapi = import_helper.import_module('_testinternalcapi')
|
||||
@ -1493,5 +1495,54 @@ class ShareableTypeTests(_GetXIDataTests):
|
||||
])
|
||||
|
||||
|
||||
class CaptureExceptionTests(unittest.TestCase):
|
||||
|
||||
# Prevent crashes with incompatible TracebackException.format().
|
||||
# Regression test for https://github.com/python/cpython/issues/143377.
|
||||
|
||||
def capture_with_formatter(self, exc, formatter):
|
||||
with support.swap_attr(traceback.TracebackException, "format", formatter):
|
||||
return _interpreters.capture_exception(exc)
|
||||
|
||||
def test_capture_exception(self):
|
||||
captured = _interpreters.capture_exception(ValueError("hello"))
|
||||
|
||||
self.assertEqual(captured.type.__name__, "ValueError")
|
||||
self.assertEqual(captured.type.__qualname__, "ValueError")
|
||||
self.assertEqual(captured.type.__module__, "builtins")
|
||||
|
||||
self.assertEqual(captured.msg, "hello")
|
||||
self.assertEqual(captured.formatted, "ValueError: hello")
|
||||
|
||||
def test_capture_exception_custom_format(self):
|
||||
exc = ValueError("good bye!")
|
||||
formatter = lambda self: ["hello\n", "world\n"]
|
||||
captured = self.capture_with_formatter(exc, formatter)
|
||||
self.assertEqual(captured.msg, "good bye!")
|
||||
self.assertEqual(captured.formatted, "ValueError: good bye!")
|
||||
self.assertEqual(captured.errdisplay, "hello\nworld")
|
||||
|
||||
@support.subTests("exc_lines", ([], ["x-no-nl"], ["x-no-nl", "y-no-nl"]))
|
||||
def test_capture_exception_invalid_format(self, exc_lines):
|
||||
formatter = lambda self: exc_lines
|
||||
captured = self.capture_with_formatter(ValueError(), formatter)
|
||||
self.assertEqual(captured.msg, "")
|
||||
self.assertEqual(captured.formatted, "ValueError: ")
|
||||
self.assertEqual(captured.errdisplay, "".join(exc_lines))
|
||||
|
||||
@unittest.skipUnless(
|
||||
support.Py_DEBUG,
|
||||
"printing subinterpreter unraisable exceptions requires DEBUG build",
|
||||
)
|
||||
def test_capture_exception_unraisable_exception(self):
|
||||
formatter = lambda self: 1
|
||||
with support.catch_unraisable_exception() as cm:
|
||||
captured = self.capture_with_formatter(ValueError(), formatter)
|
||||
self.assertNotHasAttr(captured, "errdisplay")
|
||||
self.assertEqual(cm.unraisable.exc_type, TypeError)
|
||||
self.assertEqual(str(cm.unraisable.exc_value),
|
||||
"can only join an iterable")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@ -0,0 +1,2 @@
|
||||
Fix a crash in :func:`!_interpreters.capture_exception` when
|
||||
the exception is incorrectly formatted. Patch by Bénédikt Tran.
|
||||
@ -1038,7 +1038,7 @@ _PyXIData_ReleaseAndRawFree(_PyXIData_t *xidata)
|
||||
/* convenience utilities */
|
||||
/*************************/
|
||||
|
||||
static const char *
|
||||
static char *
|
||||
_copy_string_obj_raw(PyObject *strobj, Py_ssize_t *p_size)
|
||||
{
|
||||
Py_ssize_t size = -1;
|
||||
@ -1139,11 +1139,16 @@ _format_TracebackException(PyObject *tbexc)
|
||||
}
|
||||
|
||||
Py_ssize_t size = -1;
|
||||
const char *formatted = _copy_string_obj_raw(formatted_obj, &size);
|
||||
char *formatted = _copy_string_obj_raw(formatted_obj, &size);
|
||||
Py_DECREF(formatted_obj);
|
||||
// We remove trailing the newline added by TracebackException.format().
|
||||
assert(formatted[size-1] == '\n');
|
||||
((char *)formatted)[size-1] = '\0';
|
||||
if (formatted == NULL || size == 0) {
|
||||
return formatted;
|
||||
}
|
||||
assert(formatted[size] == '\0');
|
||||
// Remove a trailing newline if needed.
|
||||
if (formatted[size-1] == '\n') {
|
||||
formatted[size-1] = '\0';
|
||||
}
|
||||
return formatted;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user