summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOleg Iarygin <oleg@arhadthedev.net>2023-05-06 22:53:48 (GMT)
committerGitHub <noreply@github.com>2023-05-06 22:53:48 (GMT)
commit42f54d1f9244784fec99e0610aa05a5051e594bb (patch)
tree9261147628e5c1543fc8d3784e34ad4dd5edaa58
parent92d8bfffbf377e91d8b92666525cb8700bb1d5e8 (diff)
downloadcpython-42f54d1f9244784fec99e0610aa05a5051e594bb.zip
cpython-42f54d1f9244784fec99e0610aa05a5051e594bb.tar.gz
cpython-42f54d1f9244784fec99e0610aa05a5051e594bb.tar.bz2
gh-101640: Make argparse _print_message catch any write error (#101802)
* In particular, don't exit when trying to print to stderr = None. * Add tests Co-authored-by: Terry Jan Reedy <tjreedy@udel.edu>
-rw-r--r--Lib/argparse.py8
-rw-r--r--Lib/test/test_argparse.py31
-rw-r--r--Misc/NEWS.d/next/Library/2023-02-09-22-24-34.gh-issue-101640.oFuEpB.rst1
3 files changed, 37 insertions, 3 deletions
diff --git a/Lib/argparse.py b/Lib/argparse.py
index a819d26..68089a5 100644
--- a/Lib/argparse.py
+++ b/Lib/argparse.py
@@ -2605,9 +2605,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
diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py
index 861da23..0659d24 100644
--- a/Lib/test/test_argparse.py
+++ b/Lib/test/test_argparse.py
@@ -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
index 0000000..917cf0f
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-02-09-22-24-34.gh-issue-101640.oFuEpB.rst
@@ -0,0 +1 @@
+:class:`argparse.ArgumentParser` now catches errors when writing messages, such as when :data:`sys.stderr` is ``None``. Patch by Oleg Iarygin.