summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorViacheslav Hletenko <seversss265@gmail.com>2026-08-14 14:25:14 +0300
committerViacheslav Hletenko <v.gletenko@vyos.io>2026-08-14 16:37:34 +0300
commit62c6eb54233dd4d09c19b6190a50be754c9e1e4f (patch)
tree05ab83712583303231bfdb0ce4e1086dead5c7d7
parentb9aa488683764efc9901b4ba1ae236f485b1c083 (diff)
downloadvyos-1x-62c6eb54233dd4d09c19b6190a50be754c9e1e4f.tar.gz
vyos-1x-62c6eb54233dd4d09c19b6190a50be754c9e1e4f.zip
utils: T9185: revert the proposed fix ask_yes_no() busy-looping forever on non-tty stdin
-rw-r--r--python/vyos/utils/io.py21
-rw-r--r--src/tests/test_utils_io.py51
2 files changed, 2 insertions, 70 deletions
diff --git a/python/vyos/utils/io.py b/python/vyos/utils/io.py
index 58ceb3476..77e30cbb0 100644
--- a/python/vyos/utils/io.py
+++ b/python/vyos/utils/io.py
@@ -55,25 +55,8 @@ def ask_input(question, default='', numeric_only=False, valid_responses=[],
return response
def ask_yes_no(question, default=False) -> bool:
- """Ask a yes/no question via input() and return their answer.
-
- Raises EOFError immediately if stdin is not a TTY (e.g. non-interactive
- scripts), instead of ever calling input(). A silent default here has
- caused real incidents (T9185): a caller that forgets to guard an
- interactive-only prompt gets no error and no visible hang, just a
- process quietly pegging a core. Callers that are meant to run
- non-interactively must check for that explicitly (see the no_prompt
- parameter used across config_mgmt.py/backend.py) rather than rely on
- this function to fall back to a default.
- """
- from sys import stdin, stdout
-
- if not stdin.isatty():
- raise EOFError(
- f'Cannot prompt "{question}" ([Y/n] default={default}): '
- 'stdin is not a TTY. Pass an explicit non-interactive option '
- '(e.g. no_prompt/-y) instead of relying on ask_yes_no().'
- )
+ """Ask a yes/no question via input() and return their answer."""
+ from sys import stdout
default_msg = "[Y/n]" if default else "[y/N]"
while True:
try:
diff --git a/src/tests/test_utils_io.py b/src/tests/test_utils_io.py
deleted file mode 100644
index 9d6471e16..000000000
--- a/src/tests/test_utils_io.py
+++ /dev/null
@@ -1,51 +0,0 @@
-# Copyright VyOS maintainers and contributors <maintainers@vyos.io>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License version 2 or later as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-import unittest
-from unittest.mock import patch
-
-from vyos.utils.io import ask_yes_no
-
-
-class TestVyOSUtilsIO(unittest.TestCase):
- def test_ask_yes_no_non_interactive_raises_without_reading_stdin(self):
- # T9185: on a non-TTY stdin (e.g. a non-interactive vbash session),
- # input() raises EOFError immediately and forever; looping on it
- # spins a core at 100% CPU. Returning a silent default instead is
- # also wrong (jestabro/dmbaturin, PR #5390): a caller that forgot
- # to guard this with an explicit non-interactive option would get
- # no error and no visible hang, just a quietly-succeeding prompt.
- # ask_yes_no() must raise immediately instead of ever calling
- # input() or returning a default.
- with patch('sys.stdin') as mock_stdin:
- mock_stdin.isatty.return_value = False
- with patch('builtins.input') as mock_input:
- self.assertRaises(EOFError, ask_yes_no, 'Proceed?', default=True)
- self.assertRaises(EOFError, ask_yes_no, 'Proceed?', default=False)
- mock_input.assert_not_called()
-
- def test_ask_yes_no_interactive_reads_input(self):
- with patch('sys.stdin') as mock_stdin:
- mock_stdin.isatty.return_value = True
- with patch('builtins.input', return_value='y'):
- self.assertTrue(ask_yes_no('Proceed?', default=False))
- with patch('builtins.input', return_value='n'):
- self.assertFalse(ask_yes_no('Proceed?', default=True))
- with patch('builtins.input', return_value=''):
- self.assertTrue(ask_yes_no('Proceed?', default=True))
- self.assertFalse(ask_yes_no('Proceed?', default=False))
-
-
-if __name__ == '__main__':
- unittest.main()