idf_monitor: use cancellation and Console.getkey from pyserial 3.3.0+

Between 3.1.0 and 3.3.0, pyserial had thread cancellation implemented
using a select, which blocked on the stdin and an auxiliary pipe. When
thread had to be cancelled, a byte would be sent into the pipe,
unblocking stdin.

Unfortunately, this method suffers from a problem with using select on
a StreamReader (which represents the decoder wrapped around stdin).
In some cases, when the TTY sends an escape sequence in response to
an escape sequence received from serial, this escape sequence will not
be read from stdin until some key is pressed.

In https://github.com/pyserial/pyserial/commit/cab3dab, this method
was replaced with an TIOCSTI ioctl. This change makes sure we use the
new cancellation method even if the script is running with older
pyserial.
This commit is contained in:
Ivan Grokhotkov 2017-08-15 16:46:22 +08:00
parent 489c523870
commit d7d6f1e376

View file

@ -43,6 +43,8 @@ import serial
import serial.tools.miniterm as miniterm
import threading
import ctypes
import types
from distutils.version import StrictVersion
key_description = miniterm.key_description
@ -157,13 +159,17 @@ class ConsoleReader(StoppableThread):
self.console.cleanup()
def _cancel(self):
if hasattr(self.console, "cancel"):
self.console.cancel()
elif os.name == 'posix':
# this is the way cancel() is implemented in pyserial 3.1 or newer,
# older pyserial doesn't have this method, hence this hack.
if os.name == 'posix':
# this is the way cancel() is implemented in pyserial 3.3 or newer,
# older pyserial (3.1+) has cancellation implemented via 'select',
# which does not work when console sends an escape sequence response
#
# even older pyserial (<3.1) does not have this method
#
# on Windows there is a different (also hacky) fix, applied above.
#
# note that TIOCSTI is not implemented in WSL / bash-on-Windows.
# TODO: introduce some workaround to make it work there.
import fcntl, termios
fcntl.ioctl(self.console.fd, termios.TIOCSTI, b'\0')
@ -221,6 +227,16 @@ class Monitor(object):
self.console.output = ANSIColorConverter(self.console.output)
self.console.byte_output = ANSIColorConverter(self.console.byte_output)
if StrictVersion(serial.VERSION) < StrictVersion('3.3.0'):
# Use Console.getkey implementation from 3.3.0 (to be in sync with the ConsoleReader._cancel patch above)
def getkey_patched(self):
c = self.enc_stdin.read(1)
if c == unichr(0x7f):
c = unichr(8) # map the BS key (which yields DEL) to backspace
return c
self.console.getkey = types.MethodType(getkey_patched, self.console)
self.serial = serial_instance
self.console_reader = ConsoleReader(self.console, self.event_queue)
self.serial_reader = SerialReader(self.serial, self.event_queue)