Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
18 changes: 16 additions & 2 deletions Lib/_pyrepl/unix_console.py
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,11 @@ def prepare(self):
raw.lflag |= termios.ISIG
raw.cc[termios.VMIN] = 1
raw.cc[termios.VTIME] = 0
tcsetattr(self.input_fd, termios.TCSADRAIN, raw)
try:
tcsetattr(self.input_fd, termios.TCSADRAIN, raw)
except termios.error as e:
if e.args[0] != errno.EIO:
raise

# In macOS terminal we need to deactivate line wrap via ANSI escape code
if platform.system() == "Darwin" and os.getenv("TERM_PROGRAM") == "Apple_Terminal":
Expand Down Expand Up @@ -368,7 +372,14 @@ def restore(self):
self.__disable_bracketed_paste()
self.__maybe_write_code(self._rmkx)
self.flushoutput()
tcsetattr(self.input_fd, termios.TCSADRAIN, self.__svtermstate)
try:
tcsetattr(self.input_fd, termios.TCSADRAIN, self.__svtermstate)
except termios.error as e:
if e.args[0] != errno.EIO:
# gh-135329: when running under external programs (like strace),
# tcsetattr may fail with EIO. We can safely ignore this
# and continue with default terminal settings.
raise
Comment thread
yihong0618 marked this conversation as resolved.
Outdated

Comment thread
yihong0618 marked this conversation as resolved.
if platform.system() == "Darwin" and os.getenv("TERM_PROGRAM") == "Apple_Terminal":
os.write(self.output_fd, b"\033[?7h")
Expand Down Expand Up @@ -407,6 +418,9 @@ def get_event(self, block: bool = True) -> Event | None:
return self.event_queue.get()
else:
continue
elif err.errno == errno.EIO:
import sys
sys.exit(errno.EIO)
Comment thread
yihong0618 marked this conversation as resolved.
Outdated
else:
raise
else:
Expand Down
55 changes: 54 additions & 1 deletion Lib/test/test_pyrepl/test_unix_console.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
import os
import sys
import unittest
import errno
Comment thread
yihong0618 marked this conversation as resolved.
Outdated
import termios
Comment thread
yihong0618 marked this conversation as resolved.
Outdated
from functools import partial
from test.support import os_helper, force_not_colorized_test_class

from unittest import TestCase
from unittest.mock import MagicMock, call, patch, ANY
from unittest.mock import MagicMock, call, patch, ANY, Mock

from .support import handle_all_events, code_to_events

Expand Down Expand Up @@ -303,3 +305,54 @@ def test_getheightwidth_with_invalid_environ(self, _os_write):
self.assertIsInstance(console.getheightwidth(), tuple)
os.environ = []
self.assertIsInstance(console.getheightwidth(), tuple)


class TestUnixConsoleEIOHandling(TestCase):

@patch('_pyrepl.unix_console.tcsetattr')
@patch('_pyrepl.unix_console.tcgetattr')
Comment thread
yihong0618 marked this conversation as resolved.
Outdated
def test_eio_error_handling_in_prepare(self, mock_tcgetattr, mock_tcsetattr):
mock_termios = Mock()
mock_termios.iflag = 0
mock_termios.oflag = 0
mock_termios.cflag = 0
mock_termios.lflag = 0
mock_termios.cc = [0] * 32
mock_termios.copy.return_value = mock_termios
mock_tcgetattr.return_value = mock_termios

mock_tcsetattr.side_effect = termios.error(errno.EIO, "Input/output error")

console = UnixConsole(term="xterm")

try:
console.prepare()
except termios.error as e:
if e.args[0] == errno.EIO:
self.fail("EIO error should have been handled gracefully in prepare()")
raise

@patch('_pyrepl.unix_console.tcsetattr')
@patch('_pyrepl.unix_console.tcgetattr')
def test_eio_error_handling_in_restore(self, mock_tcgetattr, mock_tcsetattr):

mock_termios = Mock()
mock_termios.iflag = 0
mock_termios.oflag = 0
mock_termios.cflag = 0
mock_termios.lflag = 0
mock_termios.cc = [0] * 32
mock_termios.copy.return_value = mock_termios
mock_tcgetattr.return_value = mock_termios

console = UnixConsole(term="xterm")
console.prepare()

mock_tcsetattr.side_effect = termios.error(errno.EIO, "Input/output error")

try:
console.restore()
except termios.error as e:
if e.args[0] == errno.EIO:
self.fail("EIO error should have been handled gracefully in restore()")
raise
Comment thread
yihong0618 marked this conversation as resolved.
Outdated
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Prevent infinite traceback loop when sending CTRL^C to Python through ``strace``.
Loading