Skip to content
Merged
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
11 changes: 7 additions & 4 deletions Lib/concurrent/futures/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,10 +309,13 @@ def wait(fs, timeout=None, return_when=ALL_COMPLETED):
def _result_or_cancel(fut, timeout=None):
try:
try:
return (fut.result(timeout), None)
except TimeoutError:
raise
except BaseException as exc:
# fut.exception() returns the call's own error but raises
# TimeoutError only for a map() timeout.
exc = fut.exception(timeout)
if exc is not None:
return (None, exc)
return (fut.result(), None)
except CancelledError as exc:
return (None, exc)
finally:
fut.cancel()
Expand Down
20 changes: 20 additions & 0 deletions Lib/test/test_concurrent_futures/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ def raiser(exception, msg='std'):
raise exception(msg)


def timeout_on_one(x):
if x == 1:
raise TimeoutError
return x


class FalseyBoolException(Exception):
def __bool__(self):
return False
Expand Down Expand Up @@ -87,6 +93,20 @@ def test_map_exception(self):
self.assertRaises(StopIteration, next, i)
self.assertRaises(StopIteration, next, i)

@warnings_helper.ignore_fork_in_thread_deprecation_warnings()
def test_map_timeout_from_callable(self):
# A TimeoutError from the callable is not the map() timeout, whether
# or not a map() timeout is set.
for timeout in (None, support.SHORT_TIMEOUT):
with self.subTest(timeout=timeout):
i = self.executor.map(timeout_on_one, [0, 1, 2, 3],
timeout=timeout)
self.assertEqual(next(i), 0)
self.assertRaises(TimeoutError, next, i)
self.assertEqual(next(i), 2)
self.assertEqual(next(i), 3)
self.assertRaises(StopIteration, next, i)

@warnings_helper.ignore_fork_in_thread_deprecation_warnings()
@support.requires_resource('walltime')
def test_map_timeout(self):
Expand Down
18 changes: 18 additions & 0 deletions Lib/test/test_multibytecodec.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,24 @@ def test_setstate_validates_input(self):
self.assertRaises(TypeError, decoder.setstate, (b"1234", "invalid"))
self.assertRaises(UnicodeDecodeError, decoder.setstate, (b"123456789", 0))

def test_setstate_invalid_designation(self):
# gh-153603: an unknown charset designation in the state must not crash
# the decoder. 0xff is not a registered charset mark and 0x21 ('!') is
# a GL byte that triggers the designation lookup.
for name in ('iso-2022-jp', 'iso-2022-kr'):
with self.subTest(codec=name):
decoder = codecs.getincrementaldecoder(name)()
decoder.setstate((b'', 0xff))
with self.assertRaises(UnicodeDecodeError) as cm:
decoder.decode(b'!', final=True)
self.assertEqual(cm.exception.reason,
'illegal multibyte sequence')
self.assertEqual((cm.exception.start, cm.exception.end), (0, 1))
# One illegal byte is reported, so error handlers still work.
decoder = codecs.getincrementaldecoder(name)(errors='replace')
decoder.setstate((b'', 0xff))
self.assertEqual(decoder.decode(b'!', final=True), '\ufffd')

class Test_StreamReader(unittest.TestCase):
def test_bug1728403(self):
try:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fix a crash in the ISO-2022 decoders when decoding a byte after an unknown
charset designation is set via the decoder's ``setstate`` method.
Patch by tonghuaroot.
13 changes: 7 additions & 6 deletions Modules/cjkcodecs/_codecs_iso2022.c
Original file line number Diff line number Diff line change
Expand Up @@ -533,15 +533,16 @@ DECODER(iso2022)
dsg = dsgcache;
else {
for (dsg = CONFIG_DESIGNATIONS;
dsg->mark != charset
#ifdef Py_DEBUG
&& dsg->mark != '\0'
#endif
; dsg++)
dsg->mark != charset && dsg->mark != '\0';
dsg++)
{
/* noop */
}
assert(dsg->mark != '\0');
if (dsg->mark == '\0') {
/* Unknown charset designation from a corrupt
setstate(); no width to trust, report one byte. */
return 1;
}
dsgcache = dsg;
}

Expand Down
Loading