diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py index f6f8089a065059b..cefa38fd7518abc 100644 --- a/Lib/test/test_codecs.py +++ b/Lib/test/test_codecs.py @@ -1,3 +1,4 @@ +import _codecs import codecs import contextlib import copy @@ -3735,6 +3736,17 @@ def test_encode_errors(self): self.assertEqual(codecs.iconv_encode(enc, 'a€b', 'xmlcharrefreplace')[0], b'a€b') + def test_encode_errors_unencodable_replacement(self): + # Encoding the replacement must not call the error handler again. + enc = self.require('ASCII') + codecs.register_error('test.iconv', lambda exc: ('€', exc.end)) + self.addCleanup(_codecs._unregister_error, 'test.iconv') + with self.assertRaises(UnicodeEncodeError) as cm: + codecs.iconv_encode(enc, 'a€b', 'test.iconv') + self.assertEqual((cm.exception.start, cm.exception.end), (1, 2)) + self.assertEqual(cm.exception.reason, + 'unable to encode error handler result') + def test_decode_errors(self): enc = self.require('ASCII') bad = b'a\xffb' diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c index 1244505db7c612d..ada9b02d690717f 100644 --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -1696,12 +1696,12 @@ _decimal.Context.copy cls: defining_class -Return a duplicate of the context with all flags cleared. +Return a duplicate of the context. [clinic start generated code]*/ static PyObject * _decimal_Context_copy_impl(PyObject *self, PyTypeObject *cls) -/*[clinic end generated code: output=31c9c8eeb0c0cf77 input=aef1c0bddabdf8f0]*/ +/*[clinic end generated code: output=31c9c8eeb0c0cf77 input=87f8b92b1c7462a5]*/ { decimal_state *state = PyType_GetModuleState(cls); diff --git a/Modules/_decimal/clinic/_decimal.c.h b/Modules/_decimal/clinic/_decimal.c.h index 965e5f8d0da9955..8ad883d8a2d14dc 100644 --- a/Modules/_decimal/clinic/_decimal.c.h +++ b/Modules/_decimal/clinic/_decimal.c.h @@ -371,7 +371,7 @@ PyDoc_STRVAR(_decimal_Context_copy__doc__, "copy($self, /)\n" "--\n" "\n" -"Return a duplicate of the context with all flags cleared."); +"Return a duplicate of the context."); #define _DECIMAL_CONTEXT_COPY_METHODDEF \ {"copy", _PyCFunction_CAST(_decimal_Context_copy), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, _decimal_Context_copy__doc__}, @@ -7100,4 +7100,4 @@ _decimal_Context_same_quantum(PyObject *context, PyTypeObject *cls, PyObject *co #ifndef _DECIMAL_CONTEXT_APPLY_METHODDEF #define _DECIMAL_CONTEXT_APPLY_METHODDEF #endif /* !defined(_DECIMAL_CONTEXT_APPLY_METHODDEF) */ -/*[clinic end generated code: output=07ec694c7fd6b048 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=718b1f6c20412350 input=a9049054013a1b77]*/ diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 4b4f7178ec9faf6..45d61c8b8b765a6 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -8520,11 +8520,19 @@ _PyUnicode_EncodeIconv(const char *encoding, PyObject *unicode, replen = PyBytes_GET_SIZE(rep); } else { - /* A str replacement is encoded through the same codec. */ + /* A str replacement is encoded through the same codec, but + strictly: handling its errors in turn could never terminate. */ assert(PyUnicode_Check(rep)); - repbytes = _PyUnicode_EncodeIconv(encoding, rep, errors); + repbytes = _PyUnicode_EncodeIconv(encoding, rep, NULL); Py_DECREF(rep); if (repbytes == NULL) { + if (PyErr_ExceptionMatches(PyExc_UnicodeEncodeError)) { + /* Report the input the caller knows about, not the + replacement. */ + PyErr_Clear(); + raise_encode_exception(&exc, encoding, unicode, pos, pos + 1, + "unable to encode error handler result"); + } goto done; } repdata = PyBytes_AS_STRING(repbytes);