Skip to content

Commit 4e4374b

Browse files
committed
fix(udp): let the emulator timeout reach the caller
_raw_read raised IOError to name a stalled emulator, but _read wraps it in `except socket.error` -- and on Python 3 IOError, OSError and socket.error are the same class. The detailed message was therefore caught two frames later, printed as "Failed to read from device" and collapsed to None, so the actionable error never reached the caller. The stated goal of the message could not be met by construction. Raise EmulatorNotResponding, which deliberately sits outside the OSError hierarchy, and re-raise it ahead of the socket.error arm.
1 parent 63484ad commit 4e4374b

1 file changed

Lines changed: 20 additions & 2 deletions

File tree

keepkeylib/transport_udp.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,18 @@
1919
# override for unattended runs with KK_UDP_TIMEOUT (seconds, 0 disables).
2020
DEFAULT_TIMEOUT = float(os.environ.get('KK_UDP_TIMEOUT', '60'))
2121

22+
class EmulatorNotResponding(Exception):
23+
"""The emulator stopped answering.
24+
25+
Deliberately NOT an IOError/OSError. On Python 3, IOError, OSError and
26+
socket.error are the same class, so the detailed timeout this transport
27+
raises was caught by _read()'s own `except socket.error`, printed as
28+
"Failed to read from device" and turned into None -- the caller never saw
29+
the actionable message. Raising outside that hierarchy is what lets it
30+
reach the caller.
31+
"""
32+
33+
2234
class FakeRead(object):
2335
# Let's pretend we have a file-like interface
2436
def __init__(self, func):
@@ -66,6 +78,12 @@ def _read(self):
6678
try:
6779
(msg_type, datalen) = self._read_headers(FakeRead(self._raw_read))
6880
return (msg_type, self._raw_read(datalen))
81+
except EmulatorNotResponding:
82+
# Actionable and already explained -- let it reach the caller
83+
# instead of collapsing it to None. Listed first because on
84+
# Python 3 the handler below would otherwise catch it: IOError,
85+
# OSError and socket.error are one class.
86+
raise
6987
except socket.error:
7088
print("Failed to read from device")
7189
return None
@@ -77,14 +95,14 @@ def _raw_read(self, length):
7795
except socket.timeout:
7896
# Name the cause. "timed out" alone sends people looking at the
7997
# test; the device is what stopped answering.
80-
raise IOError(
98+
raise EmulatorNotResponding(
8199
'No response from the emulator at %s:%d after %gs -- it is '
82100
'not running, has crashed, or is wedged on a confirm screen '
83101
'nothing acknowledged. Set KK_UDP_TIMEOUT to change or 0 to '
84102
'disable.' % (self.device[0], self.device[1],
85103
DEFAULT_TIMEOUT))
86104
if not data:
87-
raise IOError('Emulator closed the connection')
105+
raise EmulatorNotResponding('Emulator closed the connection')
88106
self.buffer += data[1:]
89107

90108
ret = self.buffer[:length]

0 commit comments

Comments
 (0)