Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Doc/library/argparse.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1564,12 +1564,12 @@ it exits and prints the error along with a usage message::
>>> # invalid option
>>> parser.parse_args(['--bar'])
usage: PROG [-h] [--foo FOO] [bar]
PROG: error: no such option: --bar
PROG: error: unrecognized arguments: --bar

>>> # wrong number of arguments
>>> parser.parse_args(['spam', 'badger'])
usage: PROG [-h] [--foo FOO] [bar]
PROG: error: extra arguments found: badger
PROG: error: unrecognized arguments: badger


Arguments containing ``-``
Expand Down Expand Up @@ -1606,7 +1606,7 @@ there are no options in the parser that look like negative numbers::
>>> # negative number options present, so -2 is an option
>>> parser.parse_args(['-2'])
usage: PROG [-h] [-1 ONE] [foo]
PROG: error: no such option: -2
PROG: error: unrecognized arguments: -2

>>> # negative number options present, so both -1s are options
>>> parser.parse_args(['-1', '-1'])
Expand Down
9 changes: 6 additions & 3 deletions Lib/argparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -2917,11 +2917,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):
Expand Down
18 changes: 18 additions & 0 deletions Lib/test/test_argparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,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 = {
Expand Down
9 changes: 7 additions & 2 deletions Lib/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import collections.abc
import copyreg
import functools
import keyword
import operator
import sys
import types
Expand Down Expand Up @@ -998,8 +999,12 @@ def _make_forward_ref(code, *, parent_fwdref=None, **kwargs):
if parent_fwdref.__owner__ is not None:
kwargs['owner'] = parent_fwdref.__owner__
forward_ref = annotationlib.ForwardRef(code, **kwargs)
# For compatibility, eagerly compile the forwardref's code.
forward_ref.__forward_code__
# For compatibility, eagerly compile the forwardref's code so that any
# SyntaxError is raised immediately rather than when the forward
# reference is evaluated. Similar to 'ForwardRef.evaluate()', we only compile
# it if necessary:
if not (code.isidentifier() and not keyword.iskeyword(code)):
forward_ref.__forward_code__
return forward_ref


Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Loading