From 66ff70347d9514cfb31dc924a687b0c148bdeb10 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Tue, 30 Apr 2019 18:17:15 +0200 Subject: [PATCH 1/3] UnixConsole: raise EOFError in case input_fd is not a TTY This is required for when the UnixConsole instance was created with stdin being a terminal, but then later not so anymore. E.g. having used pdbpp before pytest captures output (`pdb.set_trace()`), and then continuing (`c`). pytest is capturing output by now then, but the debugging plugin (which suspends capturing) might not be active yet (pytest_load_initial_conftests). This patch makes it raise `BdbQuit` then "correctly". --- pyrepl/reader.py | 2 +- pyrepl/unix_console.py | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pyrepl/reader.py b/pyrepl/reader.py index e1db998..dae4b5e 100644 --- a/pyrepl/reader.py +++ b/pyrepl/reader.py @@ -478,8 +478,8 @@ def prepare(self): """Get ready to run. Call restore when finished. You must not write to the console in between the calls to prepare and restore.""" + self.console.prepare() try: - self.console.prepare() self.arg = None self.screeninfo = [] self.finished = 0 diff --git a/pyrepl/unix_console.py b/pyrepl/unix_console.py index 281c200..2a2093f 100644 --- a/pyrepl/unix_console.py +++ b/pyrepl/unix_console.py @@ -354,7 +354,11 @@ def move_cursor(self, x, y): def prepare(self): # per-readline preparations: - self.__svtermstate = tcgetattr(self.input_fd) + try: + self.__svtermstate = tcgetattr(self.input_fd) + except termios.error: # (25, 'Inappropriate ioctl for device') + # assert not os.fdopen(self.input_fd).isatty() + raise EOFError raw = self.__svtermstate.copy() raw.iflag |= termios.ICRNL raw.iflag &= ~(termios.BRKINT | termios.INPCK | From d5e126dfe9b1a5b2671cb6cb9fb1acc042360d4f Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Tue, 30 Apr 2019 18:59:34 +0200 Subject: [PATCH 2/3] more defensive --- pyrepl/reader.py | 2 +- pyrepl/unix_console.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/pyrepl/reader.py b/pyrepl/reader.py index dae4b5e..e1db998 100644 --- a/pyrepl/reader.py +++ b/pyrepl/reader.py @@ -478,8 +478,8 @@ def prepare(self): """Get ready to run. Call restore when finished. You must not write to the console in between the calls to prepare and restore.""" - self.console.prepare() try: + self.console.prepare() self.arg = None self.screeninfo = [] self.finished = 0 diff --git a/pyrepl/unix_console.py b/pyrepl/unix_console.py index 2a2093f..57fe48a 100644 --- a/pyrepl/unix_console.py +++ b/pyrepl/unix_console.py @@ -359,6 +359,7 @@ def prepare(self): except termios.error: # (25, 'Inappropriate ioctl for device') # assert not os.fdopen(self.input_fd).isatty() raise EOFError + self._prepared = True raw = self.__svtermstate.copy() raw.iflag |= termios.ICRNL raw.iflag &= ~(termios.BRKINT | termios.INPCK | @@ -390,6 +391,9 @@ def prepare(self): pass def restore(self): + if not hasattr(self, '_prepared'): + return + del self._prepared self.__maybe_write_code(self._rmkx) self.flushoutput() tcsetattr(self.input_fd, termios.TCSADRAIN, self.__svtermstate) From 7349f1a03efdec348321bf481d5e6d5c93b789c0 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Fri, 25 Oct 2019 07:21:14 +0200 Subject: [PATCH 3/3] improve, add test --- pyrepl/unix_console.py | 5 ++--- testing/test_unix_console.py | 12 ++++++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 testing/test_unix_console.py diff --git a/pyrepl/unix_console.py b/pyrepl/unix_console.py index 57fe48a..afd107d 100644 --- a/pyrepl/unix_console.py +++ b/pyrepl/unix_console.py @@ -356,9 +356,8 @@ def prepare(self): # per-readline preparations: try: self.__svtermstate = tcgetattr(self.input_fd) - except termios.error: # (25, 'Inappropriate ioctl for device') - # assert not os.fdopen(self.input_fd).isatty() - raise EOFError + except termios.error as exc: + raise EOFError("could not prepare fd %d: %s" % (self.input_fd, exc)) self._prepared = True raw = self.__svtermstate.copy() raw.iflag |= termios.ICRNL diff --git a/testing/test_unix_console.py b/testing/test_unix_console.py new file mode 100644 index 0000000..51bf874 --- /dev/null +++ b/testing/test_unix_console.py @@ -0,0 +1,12 @@ +import pytest + + +def test_eoferror(): + from pyrepl.unix_console import UnixConsole + + console = UnixConsole(f_in=99) + with pytest.raises( + EOFError, + match="^could not prepare fd 99: .*Bad file descriptor" + ): + console.prepare()