diff --git a/Lib/argparse.py b/Lib/argparse.py index 29e6ebb9634261a..caa9da9eb7687fe 100644 --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -2885,11 +2885,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 1dc3f538f4ad8ba..18348bd841edffa 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -79,6 +79,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 000000000000000..5f520140f43a8ea --- /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.