Skip to content

Commit cf1c25f

Browse files
miss-islingtonarhadthedevterryjreedy
authored
[3.11] pythongh-101640: Make argparse _print_message catch any write error (pythonGH-101802) (python#104250)
pythongh-101640: Make argparse _print_message catch any write error (pythonGH-101802) * In particular, don't exit when trying to print to stderr = None. * Add tests (cherry picked from commit 42f54d1) Co-authored-by: Oleg Iarygin <[email protected]> Co-authored-by: Terry Jan Reedy <[email protected]>
1 parent 10ee19b commit cf1c25f

File tree

3 files changed

+37
-3
lines changed

3 files changed

+37
-3
lines changed

Lib/argparse.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -2602,9 +2602,11 @@ def print_help(self, file=None):
26022602

26032603
def _print_message(self, message, file=None):
26042604
if message:
2605-
if file is None:
2606-
file = _sys.stderr
2607-
file.write(message)
2605+
file = file or _sys.stderr
2606+
try:
2607+
file.write(message)
2608+
except (AttributeError, OSError):
2609+
pass
26082610

26092611
# ===============
26102612
# Exiting methods

Lib/test/test_argparse.py

+31
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Author: Steven J. Bethard <[email protected]>.
22

3+
import contextlib
4+
import functools
35
import inspect
46
import io
57
import operator
@@ -35,6 +37,35 @@ def getvalue(self):
3537
return self.buffer.raw.getvalue().decode('utf-8')
3638

3739

40+
class StdStreamTest(unittest.TestCase):
41+
42+
def test_skip_invalid_stderr(self):
43+
parser = argparse.ArgumentParser()
44+
with (
45+
contextlib.redirect_stderr(None),
46+
mock.patch('argparse._sys.exit')
47+
):
48+
parser.exit(status=0, message='foo')
49+
50+
def test_skip_invalid_stdout(self):
51+
parser = argparse.ArgumentParser()
52+
for func in (
53+
parser.print_usage,
54+
parser.print_help,
55+
functools.partial(parser.parse_args, ['-h'])
56+
):
57+
with (
58+
self.subTest(func=func),
59+
contextlib.redirect_stdout(None),
60+
# argparse uses stderr as a fallback
61+
StdIOBuffer() as mocked_stderr,
62+
contextlib.redirect_stderr(mocked_stderr),
63+
mock.patch('argparse._sys.exit'),
64+
):
65+
func()
66+
self.assertRegex(mocked_stderr.getvalue(), r'usage:')
67+
68+
3869
class TestCase(unittest.TestCase):
3970

4071
def setUp(self):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
:class:`argparse.ArgumentParser` now catches errors when writing messages, such as when :data:`sys.stderr` is ``None``. Patch by Oleg Iarygin.

0 commit comments

Comments
 (0)