]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.11] gh-101640: Make argparse _print_message catch any write error (GH-101802)...
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Sat, 6 May 2023 23:17:27 +0000 (16:17 -0700)
committerGitHub <noreply@github.com>
Sat, 6 May 2023 23:17:27 +0000 (23:17 +0000)
gh-101640: Make argparse _print_message catch any write error (GH-101802)

* In particular, don't exit when trying to print to stderr = None.
* Add tests

(cherry picked from commit 42f54d1f9244784fec99e0610aa05a5051e594bb)

Co-authored-by: Oleg Iarygin <oleg@arhadthedev.net>
Co-authored-by: Terry Jan Reedy <tjreedy@udel.edu>
Lib/argparse.py
Lib/test/test_argparse.py
Misc/NEWS.d/next/Library/2023-02-09-22-24-34.gh-issue-101640.oFuEpB.rst [new file with mode: 0644]

index 96617464966f796d444c9ecf11117ff02cc7dd72..9962e61a679ebeab7f537aad6ddedbb1224f83ed 100644 (file)
@@ -2602,9 +2602,11 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
 
     def _print_message(self, message, file=None):
         if message:
-            if file is None:
-                file = _sys.stderr
-            file.write(message)
+            file = file or _sys.stderr
+            try:
+                file.write(message)
+            except (AttributeError, OSError):
+                pass
 
     # ===============
     # Exiting methods
index 3fb88e5c637122c4856adaed836042d1e6d43f40..6322ebbb8405ee26bfa843d65e379da131b9d484 100644 (file)
@@ -1,5 +1,7 @@
 # Author: Steven J. Bethard <steven.bethard@gmail.com>.
 
+import contextlib
+import functools
 import inspect
 import io
 import operator
@@ -35,6 +37,35 @@ class StdIOBuffer(io.TextIOWrapper):
         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):
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 (file)
index 0000000..917cf0f
--- /dev/null
@@ -0,0 +1 @@
+:class:`argparse.ArgumentParser` now catches errors when writing messages, such as when :data:`sys.stderr` is ``None``. Patch by Oleg Iarygin.