diff options
| author | Viacheslav Hletenko <seversss265@gmail.com> | 2026-08-14 14:25:14 +0300 |
|---|---|---|
| committer | Viacheslav Hletenko <v.gletenko@vyos.io> | 2026-08-14 16:37:34 +0300 |
| commit | 62c6eb54233dd4d09c19b6190a50be754c9e1e4f (patch) | |
| tree | 05ab83712583303231bfdb0ce4e1086dead5c7d7 | |
| parent | b9aa488683764efc9901b4ba1ae236f485b1c083 (diff) | |
| download | vyos-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.py | 21 | ||||
| -rw-r--r-- | src/tests/test_utils_io.py | 51 |
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() |
