From d7d6f1e376ac40731edd26b93a61fda8cbc2b2a9 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Tue, 15 Aug 2017 16:46:22 +0800 Subject: [PATCH] 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. --- tools/idf_monitor.py | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/tools/idf_monitor.py b/tools/idf_monitor.py index b813d83e1..7ddee831f 100755 --- a/tools/idf_monitor.py +++ b/tools/idf_monitor.py @@ -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)