Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 103 additions & 0 deletions Lib/test/test_capi/test_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,109 @@ def _test_unicode_error_set_end(self, literal, exc_type, set_end):
self.assertEqual(exc.end, new_end)
self._check_no_crash(exc)

def test_unicode_encode_error_get_object(self):
get_object = _testcapi.unicode_encode_get_object
self._test_unicode_error_get_object('x', UnicodeEncodeError, get_object)

def test_unicode_decode_error_get_object(self):
get_object = _testcapi.unicode_decode_get_object
self._test_unicode_error_get_object(b'x', UnicodeDecodeError, get_object)

def test_unicode_translate_error_get_object(self):
get_object = _testcapi.unicode_translate_get_object
self._test_unicode_error_get_object('x', TestUnicodeTranslateError, get_object)

def _test_unicode_error_get_object(self, literal, exc_type, get_object):
for obj_len in range(5):
obj = literal * obj_len
with self.subTest(obj, exc_type=exc_type):
exc = exc_type('utf-8', obj, 0, obj_len, 'reason')
self.assertEqual(get_object(exc), obj)
# the C API getter agrees with the attribute
self.assertEqual(get_object(exc), exc.object)
self._check_no_crash(exc)

def test_unicode_encode_error_get_reason(self):
get_reason = _testcapi.unicode_encode_get_reason
self._test_unicode_error_get_reason('x', UnicodeEncodeError, get_reason)

def test_unicode_decode_error_get_reason(self):
get_reason = _testcapi.unicode_decode_get_reason
self._test_unicode_error_get_reason(b'x', UnicodeDecodeError, get_reason)

def test_unicode_translate_error_get_reason(self):
get_reason = _testcapi.unicode_translate_get_reason
self._test_unicode_error_get_reason('x', TestUnicodeTranslateError, get_reason)

def _test_unicode_error_get_reason(self, literal, exc_type, get_reason):
obj_len = 5
obj = literal * obj_len
for reason in ['reason', '', 'a' * 100, 'non-ascii \xe9']:
with self.subTest(exc_type=exc_type, reason=reason):
exc = exc_type('utf-8', obj, 0, obj_len, reason)
self.assertEqual(get_reason(exc), reason)
# the C API getter agrees with the attribute
self.assertEqual(get_reason(exc), exc.reason)
self._check_no_crash(exc)

def test_unicode_encode_error_set_reason(self):
set_reason = _testcapi.unicode_encode_set_reason
self._test_unicode_error_set_reason('x', UnicodeEncodeError, set_reason)

def test_unicode_decode_error_set_reason(self):
set_reason = _testcapi.unicode_decode_set_reason
self._test_unicode_error_set_reason(b'x', UnicodeDecodeError, set_reason)

def test_unicode_translate_error_set_reason(self):
set_reason = _testcapi.unicode_translate_set_reason
self._test_unicode_error_set_reason('x', TestUnicodeTranslateError, set_reason)

def _test_unicode_error_set_reason(self, literal, exc_type, set_reason):
obj_len = 5
obj = literal * obj_len
for new_reason in ['new reason', '', 'a' * 100]:
with self.subTest('C-API', exc_type=exc_type, reason=new_reason):
exc = exc_type('utf-8', obj, 0, obj_len, 'reason')
set_reason(exc, new_reason)
self.assertEqual(exc.reason, new_reason)
self._check_no_crash(exc)

with self.subTest('Py-API', exc_type=exc_type, reason=new_reason):
exc = exc_type('utf-8', obj, 0, obj_len, 'reason')
exc.reason = new_reason
self.assertEqual(exc.reason, new_reason)
self._check_no_crash(exc)

def test_unicode_error_object_reason_bad_type(self):
# the C API getters and setters require a UnicodeError instance
bad_objects = [ValueError('x'), 'not an exception', 42, None]
getters = [
('UnicodeEncodeError', _testcapi.unicode_encode_get_object),
('UnicodeDecodeError', _testcapi.unicode_decode_get_object),
('UnicodeTranslateError', _testcapi.unicode_translate_get_object),
('UnicodeEncodeError', _testcapi.unicode_encode_get_reason),
('UnicodeDecodeError', _testcapi.unicode_decode_get_reason),
('UnicodeTranslateError', _testcapi.unicode_translate_get_reason),
]
for expect_type, getter in getters:
regex = f'expecting a {expect_type} object'
for bad in bad_objects:
with self.subTest(getter=getter, bad=bad):
with self.assertRaisesRegex(TypeError, regex):
getter(bad)

setters = [
('UnicodeEncodeError', _testcapi.unicode_encode_set_reason),
('UnicodeDecodeError', _testcapi.unicode_decode_set_reason),
('UnicodeTranslateError', _testcapi.unicode_translate_set_reason),
]
for expect_type, setter in setters:
regex = f'expecting a {expect_type} object'
for bad in bad_objects:
with self.subTest(setter=setter, bad=bad):
with self.assertRaisesRegex(TypeError, regex):
setter(bad, 'reason')


class Test_PyUnstable_Exc_PrepReraiseStar(ExceptionIsLikeMixin, unittest.TestCase):

Expand Down
96 changes: 96 additions & 0 deletions Modules/_testcapi/exceptions.c
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,93 @@ unicode_translate_set_end(PyObject *Py_UNUSED(module), PyObject *args)
Py_RETURN_NONE;
}

/* Test PyUnicodeEncodeError_GetObject */
static PyObject *
unicode_encode_get_object(PyObject *Py_UNUSED(module), PyObject *arg)
{
return PyUnicodeEncodeError_GetObject(arg);
}

/* Test PyUnicodeDecodeError_GetObject */
static PyObject *
unicode_decode_get_object(PyObject *Py_UNUSED(module), PyObject *arg)
{
return PyUnicodeDecodeError_GetObject(arg);
}

/* Test PyUnicodeTranslateError_GetObject */
static PyObject *
unicode_translate_get_object(PyObject *Py_UNUSED(module), PyObject *arg)
{
return PyUnicodeTranslateError_GetObject(arg);
}

/* Test PyUnicodeEncodeError_GetReason */
static PyObject *
unicode_encode_get_reason(PyObject *Py_UNUSED(module), PyObject *arg)
{
return PyUnicodeEncodeError_GetReason(arg);
}

/* Test PyUnicodeDecodeError_GetReason */
static PyObject *
unicode_decode_get_reason(PyObject *Py_UNUSED(module), PyObject *arg)
{
return PyUnicodeDecodeError_GetReason(arg);
}

/* Test PyUnicodeTranslateError_GetReason */
static PyObject *
unicode_translate_get_reason(PyObject *Py_UNUSED(module), PyObject *arg)
{
return PyUnicodeTranslateError_GetReason(arg);
}

/* Test PyUnicodeEncodeError_SetReason */
static PyObject *
unicode_encode_set_reason(PyObject *Py_UNUSED(module), PyObject *args)
{
PyObject *exc;
const char *reason;
if (!PyArg_ParseTuple(args, "Os", &exc, &reason)) {
return NULL;
}
if (PyUnicodeEncodeError_SetReason(exc, reason) < 0) {
return NULL;
}
Py_RETURN_NONE;
}

/* Test PyUnicodeDecodeError_SetReason */
static PyObject *
unicode_decode_set_reason(PyObject *Py_UNUSED(module), PyObject *args)
{
PyObject *exc;
const char *reason;
if (!PyArg_ParseTuple(args, "Os", &exc, &reason)) {
return NULL;
}
if (PyUnicodeDecodeError_SetReason(exc, reason) < 0) {
return NULL;
}
Py_RETURN_NONE;
}

/* Test PyUnicodeTranslateError_SetReason */
static PyObject *
unicode_translate_set_reason(PyObject *Py_UNUSED(module), PyObject *args)
{
PyObject *exc;
const char *reason;
if (!PyArg_ParseTuple(args, "Os", &exc, &reason)) {
return NULL;
}
if (PyUnicodeTranslateError_SetReason(exc, reason) < 0) {
return NULL;
}
Py_RETURN_NONE;
}

/*
* Define the PyRecurdingInfinitelyError_Type
*/
Expand Down Expand Up @@ -572,6 +659,15 @@ static PyMethodDef test_methods[] = {
{"unicode_encode_set_end", unicode_encode_set_end, METH_VARARGS},
{"unicode_decode_set_end", unicode_decode_set_end, METH_VARARGS},
{"unicode_translate_set_end", unicode_translate_set_end, METH_VARARGS},
{"unicode_encode_get_object", unicode_encode_get_object, METH_O},
{"unicode_decode_get_object", unicode_decode_get_object, METH_O},
{"unicode_translate_get_object", unicode_translate_get_object, METH_O},
{"unicode_encode_get_reason", unicode_encode_get_reason, METH_O},
{"unicode_decode_get_reason", unicode_decode_get_reason, METH_O},
{"unicode_translate_get_reason", unicode_translate_get_reason, METH_O},
{"unicode_encode_set_reason", unicode_encode_set_reason, METH_VARARGS},
{"unicode_decode_set_reason", unicode_decode_set_reason, METH_VARARGS},
{"unicode_translate_set_reason", unicode_translate_set_reason, METH_VARARGS},
{NULL},
};

Expand Down
Loading