From d42f58c943ad66c17fb25da87bc8bacaff438886 Mon Sep 17 00:00:00 2001 From: Oleg Iarygin Date: Fri, 10 Feb 2023 19:51:22 +0400 Subject: [PATCH 1/3] Add tests --- Lib/test/test_argparse.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py index cabb2f837693ff..0a2e5e4a2acede 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -1,5 +1,7 @@ # Author: Steven J. Bethard . +import contextlib +import functools import inspect import io import operator @@ -35,6 +37,35 @@ def getvalue(self): return self.buffer.raw.getvalue().decode('utf-8') +class StdStreamTest(unittest.TestCase): + + def test_skip_invalid_stderr(self): + parser = argparse.ArgumentParser() + with ( + contextlib.redirect_stderr(None), + mock.patch('argparse._sys.exit') + ): + parser.exit(status=0, message='foo') + + def test_skip_invalid_stdout(self): + parser = argparse.ArgumentParser() + for func in ( + parser.print_usage, + parser.print_help, + functools.partial(parser.parse_args, ['-h']) + ): + with ( + self.subTest(func=func), + contextlib.redirect_stdout(None), + # argparse uses stderr as a fallback + StdIOBuffer() as mocked_stderr, + contextlib.redirect_stderr(mocked_stderr), + mock.patch('argparse._sys.exit'), + ): + func() + self.assertRegex(mocked_stderr.getvalue(), r'usage:') + + class TestCase(unittest.TestCase): def setUp(self): From 1e3fb87dd26bc9228461c392e87c8ebd7d08ad58 Mon Sep 17 00:00:00 2001 From: Oleg Iarygin Date: Thu, 9 Feb 2023 22:27:19 +0400 Subject: [PATCH 2/3] Make argparse silent on `sys.stderr` set to `None` --- Lib/argparse.py | 6 +++--- .../Library/2023-02-09-22-24-34.gh-issue-101640.oFuEpB.rst | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-02-09-22-24-34.gh-issue-101640.oFuEpB.rst diff --git a/Lib/argparse.py b/Lib/argparse.py index 240625ff01084e..998d01d2cfb1f1 100644 --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -2598,9 +2598,9 @@ def print_help(self, file=None): def _print_message(self, message, file=None): if message: - if file is None: - file = _sys.stderr - file.write(message) + file = file or _sys.stderr + if file is not None: + file.write(message) # =============== # Exiting methods diff --git a/Misc/NEWS.d/next/Library/2023-02-09-22-24-34.gh-issue-101640.oFuEpB.rst b/Misc/NEWS.d/next/Library/2023-02-09-22-24-34.gh-issue-101640.oFuEpB.rst new file mode 100644 index 00000000000000..57c45e88cbec52 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-02-09-22-24-34.gh-issue-101640.oFuEpB.rst @@ -0,0 +1,2 @@ +:class:`argparse.ArgumentParser` no longer tries to use :data:`sys.stderr` +when it is set to ``None``. Patch by Oleg Iarygin. From 182350cef64b55e35f9ed997691586f770a8b2ba Mon Sep 17 00:00:00 2001 From: Oleg Iarygin Date: Sat, 6 May 2023 22:32:19 +0400 Subject: [PATCH 3/3] Apply suggestions from code review Co-authored-by: Terry Jan Reedy --- Lib/argparse.py | 4 +++- .../Library/2023-02-09-22-24-34.gh-issue-101640.oFuEpB.rst | 3 +-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Lib/argparse.py b/Lib/argparse.py index 24b32bf539d541..68089a5c1e80b0 100644 --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -2606,8 +2606,10 @@ def print_help(self, file=None): def _print_message(self, message, file=None): if message: file = file or _sys.stderr - if file is not None: + try: file.write(message) + except (AttributeError, OSError): + pass # =============== # Exiting methods diff --git a/Misc/NEWS.d/next/Library/2023-02-09-22-24-34.gh-issue-101640.oFuEpB.rst b/Misc/NEWS.d/next/Library/2023-02-09-22-24-34.gh-issue-101640.oFuEpB.rst index 57c45e88cbec52..917cf0f97b9e06 100644 --- a/Misc/NEWS.d/next/Library/2023-02-09-22-24-34.gh-issue-101640.oFuEpB.rst +++ b/Misc/NEWS.d/next/Library/2023-02-09-22-24-34.gh-issue-101640.oFuEpB.rst @@ -1,2 +1 @@ -:class:`argparse.ArgumentParser` no longer tries to use :data:`sys.stderr` -when it is set to ``None``. Patch by Oleg Iarygin. +:class:`argparse.ArgumentParser` now catches errors when writing messages, such as when :data:`sys.stderr` is ``None``. Patch by Oleg Iarygin.