diff --git a/Lib/argparse.py b/Lib/argparse.py index fe9fde7f65830f..38e1c0d0ed78fd 100644 --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -2917,11 +2917,14 @@ def print_help(self, file=None): self._print_message(help_text, file) def _print_message(self, message, file=None): - if message: - file = file or _sys.stderr + if not message: + return + if file is None: + file = _sys.stderr + if file is not None: try: file.write(message) - except (AttributeError, OSError): + except OSError: pass def _get_theme(self, file=None): diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index 1193cdca3e4e19..75beb5ede13fef 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -80,6 +80,24 @@ def test_skip_invalid_stdout(self): func() self.assertRegex(mocked_stderr.getvalue(), r'usage:') + def test_invalid_file_only(self): + parser = argparse.ArgumentParser() + for func in (parser.print_usage, parser.print_help): + for invalid_f in ("invalid file", "", 0): + with ( + self.subTest(func=func, invalid_f=invalid_f), + self.assertRaises(AttributeError), + ): + func(file=invalid_f) + + def test_exit_when_stderr_oserror(self): + parser = argparse.ArgumentParser() + with (mock.patch('argparse._sys.stderr.write', + side_effect=OSError('not raise this')), + self.assertRaises(SystemExit), + ): + parser.exit(status=0, message='foo') + class TestLazyImports(unittest.TestCase): LAZY_IMPORTS = { diff --git a/Misc/NEWS.d/next/Library/2026-07-18-16-05-38.gh-issue-153967.-OUNXe.rst b/Misc/NEWS.d/next/Library/2026-07-18-16-05-38.gh-issue-153967.-OUNXe.rst new file mode 100644 index 00000000000000..5f520140f43a8e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-07-18-16-05-38.gh-issue-153967.-OUNXe.rst @@ -0,0 +1,3 @@ +:meth:`argparse.ArgumentParser.print_usage` and +:meth:`argparse.ArgumentParser.print_help` won't silently fail when an invalid +file object is specified. Patch by Timothy Poon.