Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 6fb8fb1

Browse files
committed
#12776,#11839: call argparse type function only once.
Before, the type function was called twice in the case where the default was specified and the argument was given as well. This was especially problematic for the FileType type, as a default file would always be opened, even if a file argument was specified on the command line. Patch by Arnaud Fontaine, with additional test by Mike Meyer.
1 parent 2a0fb14 commit 6fb8fb1

4 files changed

Lines changed: 69 additions & 7 deletions

File tree

Lib/argparse.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1714,10 +1714,7 @@ def parse_known_args(self, args=None, namespace=None):
17141714
if action.dest is not SUPPRESS:
17151715
if not hasattr(namespace, action.dest):
17161716
if action.default is not SUPPRESS:
1717-
default = action.default
1718-
if isinstance(action.default, str):
1719-
default = self._get_value(action, default)
1720-
setattr(namespace, action.dest, default)
1717+
setattr(namespace, action.dest, action.default)
17211718

17221719
# add any parser defaults that aren't present
17231720
for dest in self._defaults:
@@ -1945,12 +1942,22 @@ def consume_positionals(start_index):
19451942
if positionals:
19461943
self.error(_('too few arguments'))
19471944

1948-
# make sure all required actions were present
1945+
# make sure all required actions were present, and convert defaults.
19491946
for action in self._actions:
1950-
if action.required:
1951-
if action not in seen_actions:
1947+
if action not in seen_actions:
1948+
if action.required:
19521949
name = _get_action_name(action)
19531950
self.error(_('argument %s is required') % name)
1951+
else:
1952+
# Convert action default now instead of doing it before
1953+
# parsing arguments to avoid calling convert functions
1954+
# twice (which may fail) if the argument was given, but
1955+
# only if it was defined already in the namespace
1956+
if (action.default is not None and
1957+
hasattr(namespace, action.dest) and
1958+
action.default is getattr(namespace, action.dest)):
1959+
setattr(namespace, action.dest,
1960+
self._get_value(action, action.default))
19541961

19551962
# make sure all required groups had one option present
19561963
for group in self._mutually_exclusive_groups:

Lib/test/test_argparse.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1462,6 +1462,22 @@ def setUp(self):
14621462
('readonly', NS(x=None, spam=RFile('readonly'))),
14631463
]
14641464

1465+
class TestFileTypeDefaults(TempDirMixin, ParserTestCase):
1466+
"""Test that a file is not created unless the default is needed"""
1467+
def setUp(self):
1468+
super(TestFileTypeDefaults, self).setUp()
1469+
file = open(os.path.join(self.temp_dir, 'good'), 'w')
1470+
file.write('good')
1471+
file.close()
1472+
1473+
argument_signatures = [
1474+
Sig('-c', type=argparse.FileType('r'), default='no-file.txt'),
1475+
]
1476+
# should provoke no such file error
1477+
failures = ['']
1478+
# should not provoke error because default file is created
1479+
successes = [('-c good', NS(c=RFile('good')))]
1480+
14651481

14661482
class TestFileTypeRB(TempDirMixin, ParserTestCase):
14671483
"""Test the FileType option/argument type for reading files"""
@@ -4468,6 +4484,38 @@ def spam(string):
44684484
else:
44694485
self.fail()
44704486

4487+
# ================================================
4488+
# Check that the type function is called only once
4489+
# ================================================
4490+
4491+
class TestTypeFunctionCallOnlyOnce(TestCase):
4492+
4493+
def test_type_function_call_only_once(self):
4494+
def spam(string_to_convert):
4495+
self.assertEqual(string_to_convert, 'spam!')
4496+
return 'foo_converted'
4497+
4498+
parser = argparse.ArgumentParser()
4499+
parser.add_argument('--foo', type=spam, default='bar')
4500+
args = parser.parse_args('--foo spam!'.split())
4501+
self.assertEqual(NS(foo='foo_converted'), args)
4502+
4503+
# ================================================================
4504+
# Check that the type function is called with a non-string default
4505+
# ================================================================
4506+
4507+
class TestTypeFunctionCallWithNonStringDefault(TestCase):
4508+
4509+
def test_type_function_call_with_non_string_default(self):
4510+
def spam(int_to_convert):
4511+
self.assertEqual(int_to_convert, 0)
4512+
return 'foo_converted'
4513+
4514+
parser = argparse.ArgumentParser()
4515+
parser.add_argument('--foo', type=spam, default=0)
4516+
args = parser.parse_args([])
4517+
self.assertEqual(NS(foo='foo_converted'), args)
4518+
44714519
# ======================
44724520
# parse_known_args tests
44734521
# ======================

Misc/ACKS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@ Nils Fischbeck
305305
Frederik Fix
306306
Matt Fleming
307307
Hernán Martínez Foffani
308+
Arnaud Fontaine
308309
Michael Foord
309310
Amaury Forgeot d'Arc
310311
Doug Fort

Misc/NEWS

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,12 @@ Core and Builtins
112112
Library
113113
-------
114114

115+
- Issue #12776,#11839: call argparse type function (specified by add_argument)
116+
only once. Before, the type function was called twice in the case where the
117+
default was specified and the argument was given as well. This was
118+
especially problematic for the FileType type, as a default file would always
119+
be opened, even if a file argument was specified on the command line.
120+
115121
- Issue #13370: Ensure that ctypes works on Mac OS X when Python is
116122
compiled using the clang compiler
117123

0 commit comments

Comments
 (0)