Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f1de55f

Browse files
committed
Also chain codec exceptions that allow weakrefs
The zlib and hex codecs throw custom exception types with weakref support if the input type is valid, but the data fails validation. Make sure the exception chaining in the codec infrastructure can wrap those as well.
1 parent a726192 commit f1de55f

2 files changed

Lines changed: 50 additions & 8 deletions

File tree

Lib/test/test_codecs.py

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2402,6 +2402,25 @@ def test_type_error_for_binary_input(self):
24022402
self.assertTrue(isinstance(failure.exception.__cause__,
24032403
AttributeError))
24042404

2405+
def test_custom_zlib_error_is_wrapped(self):
2406+
# Check zlib codec gives a good error for malformed input
2407+
msg = "^decoding with 'zlib_codec' codec failed"
2408+
with self.assertRaisesRegex(Exception, msg) as failure:
2409+
b"hello".decode("zlib_codec")
2410+
self.assertTrue(isinstance(failure.exception.__cause__,
2411+
type(failure.exception)))
2412+
2413+
def test_custom_hex_error_is_wrapped(self):
2414+
# Check hex codec gives a good error for malformed input
2415+
msg = "^decoding with 'hex_codec' codec failed"
2416+
with self.assertRaisesRegex(Exception, msg) as failure:
2417+
b"hello".decode("hex_codec")
2418+
self.assertTrue(isinstance(failure.exception.__cause__,
2419+
type(failure.exception)))
2420+
2421+
# Unfortunately, the bz2 module throws OSError, which the codec
2422+
# machinery currently can't wrap :(
2423+
24052424
def test_bad_decoding_output_type(self):
24062425
# Check bytes.decode and bytearray.decode give a good error
24072426
# message for binary -> binary codecs
@@ -2466,15 +2485,15 @@ def assertWrapped(self, operation, exc_type, msg):
24662485
with self.assertRaisesRegex(exc_type, full_msg) as caught:
24672486
yield caught
24682487

2469-
def check_wrapped(self, obj_to_raise, msg):
2488+
def check_wrapped(self, obj_to_raise, msg, exc_type=RuntimeError):
24702489
self.set_codec(obj_to_raise)
2471-
with self.assertWrapped("encoding", RuntimeError, msg):
2490+
with self.assertWrapped("encoding", exc_type, msg):
24722491
"str_input".encode(self.codec_name)
2473-
with self.assertWrapped("encoding", RuntimeError, msg):
2492+
with self.assertWrapped("encoding", exc_type, msg):
24742493
codecs.encode("str_input", self.codec_name)
2475-
with self.assertWrapped("decoding", RuntimeError, msg):
2494+
with self.assertWrapped("decoding", exc_type, msg):
24762495
b"bytes input".decode(self.codec_name)
2477-
with self.assertWrapped("decoding", RuntimeError, msg):
2496+
with self.assertWrapped("decoding", exc_type, msg):
24782497
codecs.decode(b"bytes input", self.codec_name)
24792498

24802499
def test_raise_by_type(self):
@@ -2484,6 +2503,18 @@ def test_raise_by_value(self):
24842503
msg = "This should be wrapped"
24852504
self.check_wrapped(RuntimeError(msg), msg)
24862505

2506+
def test_raise_grandchild_subclass_exact_size(self):
2507+
msg = "This should be wrapped"
2508+
class MyRuntimeError(RuntimeError):
2509+
__slots__ = ()
2510+
self.check_wrapped(MyRuntimeError(msg), msg, MyRuntimeError)
2511+
2512+
def test_raise_subclass_with_weakref_support(self):
2513+
msg = "This should be wrapped"
2514+
class MyRuntimeError(RuntimeError):
2515+
pass
2516+
self.check_wrapped(MyRuntimeError(msg), msg, MyRuntimeError)
2517+
24872518
@contextlib.contextmanager
24882519
def assertNotWrapped(self, operation, exc_type, msg_re, msg=None):
24892520
if msg is None:

Objects/exceptions.c

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2630,16 +2630,27 @@ _PyErr_TrySetFromCause(const char *format, ...)
26302630
PyTypeObject *caught_type;
26312631
PyObject **dictptr;
26322632
PyObject *instance_args;
2633-
Py_ssize_t num_args;
2633+
Py_ssize_t num_args, caught_type_size, base_exc_size;
26342634
PyObject *new_exc, *new_val, *new_tb;
26352635
va_list vargs;
2636+
int same_basic_size;
26362637

26372638
PyErr_Fetch(&exc, &val, &tb);
26382639
caught_type = (PyTypeObject *)exc;
2639-
/* Ensure type info indicates no extra state is stored at the C level */
2640+
/* Ensure type info indicates no extra state is stored at the C level
2641+
* and that the type can be reinstantiated using PyErr_Format
2642+
*/
2643+
caught_type_size = caught_type->tp_basicsize;
2644+
base_exc_size = _PyExc_BaseException.tp_basicsize;
2645+
same_basic_size = (
2646+
caught_type_size == base_exc_size ||
2647+
(PyType_SUPPORTS_WEAKREFS(caught_type) &&
2648+
(caught_type_size == base_exc_size + sizeof(PyObject *))
2649+
)
2650+
);
26402651
if (caught_type->tp_init != (initproc)BaseException_init ||
26412652
caught_type->tp_new != BaseException_new ||
2642-
caught_type->tp_basicsize != _PyExc_BaseException.tp_basicsize ||
2653+
!same_basic_size ||
26432654
caught_type->tp_itemsize != _PyExc_BaseException.tp_itemsize) {
26442655
/* We can't be sure we can wrap this safely, since it may contain
26452656
* more state than just the exception type. Accordingly, we just

0 commit comments

Comments
 (0)