Skip to content

Commit 76edae9

Browse files
committed
argparse subnamespace: Change implementation and tests from str to bool
1 parent f62fbff commit 76edae9

2 files changed

Lines changed: 42 additions & 43 deletions

File tree

Lib/argparse.py

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1317,7 +1317,7 @@ def __init__(self,
13171317
help=help,
13181318
metavar=metavar)
13191319

1320-
def add_parser(self, name, *, deprecated=False, subnamespace=None, **kwargs):
1320+
def add_parser(self, name, *, deprecated=False, subnamespace=False, **kwargs):
13211321
# set prog from the existing prefix
13221322
if kwargs.get('prog') is None:
13231323
kwargs['prog'] = '%s %s' % (self._prog_prefix, name)
@@ -1348,10 +1348,10 @@ def add_parser(self, name, *, deprecated=False, subnamespace=None, **kwargs):
13481348
parser._check_help(choice_action)
13491349
self._name_parser_map[name] = parser
13501350

1351-
# add the subnamespace attribute to the parser if specified
1352-
# for nested namespaces
1353-
if subnamespace is not None:
1354-
setattr(parser, 'subnamespace', subnamespace)
1351+
# set the subnamespace attribute on the parser to determine
1352+
# whether parsed arguments should be stored in their own
1353+
# nested namespace or added to the parent parser's namespace
1354+
setattr(parser, 'subnamespace', subnamespace)
13551355

13561356
# make parser available under aliases also
13571357
for alias in aliases:
@@ -1396,13 +1396,18 @@ def __call__(self, parser, namespace, values, option_string=None):
13961396
# namespace for the relevant parts.
13971397
subnamespace, arg_strings = subparser.parse_known_args(arg_strings, None)
13981398

1399-
# If a subnamespace name has been specified for the subparser
1400-
# then store the subparser's namespace within the parent namespace
1401-
# using that name. Otherwise update the parent namespace with the
1402-
# values from the subnamespace.
1403-
subnamespace_name = getattr(subparser, 'subnamespace', None)
1404-
if subnamespace_name is not None:
1405-
subnamespace_name = subnamespace_name.replace('-', '_')
1399+
# If the subparser's 'subnamespace' attribute is ``True``
1400+
# then store the subparser's parsed arguments contained in
1401+
# their own namespace, nested within the parent namespace.
1402+
# The attribute name in the parent namespace at which the
1403+
# subparser's subnamespace is stored is the subparser's name,
1404+
# specified when using '.add_parser()', but with '-' replaced with '_'
1405+
# similar to how options are stored.
1406+
#
1407+
# Otherwise if 'subnamespace' is ``False`` then update
1408+
# the parent namespace with the values from the subnamespace.
1409+
if subparser.subnamespace:
1410+
subnamespace_name = parser_name.replace('-', '_')
14061411
setattr(namespace, subnamespace_name, subnamespace)
14071412
else:
14081413
for key, value in vars(subnamespace).items():

Lib/test/test_argparse.py

Lines changed: 25 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6174,10 +6174,10 @@ def test_single_subnamespace(self):
61746174
parser = argparse.ArgumentParser()
61756175
action = parser.add_subparsers(required=False, dest="action")
61766176

6177-
parser_add = action.add_parser("add", subnamespace="add")
6177+
parser_add = action.add_parser("add", subnamespace=True)
61786178
parser_add.add_argument("--to")
61796179

6180-
parser_remove = action.add_parser("remove", subnamespace="remove")
6180+
parser_remove = action.add_parser("remove", subnamespace=True)
61816181
parser_remove.add_argument("--from")
61826182

61836183
# a root parser should not have 'subnamespace' attribute,
@@ -6189,8 +6189,8 @@ def test_single_subnamespace(self):
61896189
# that was set when calling `action.add_parser()`
61906190
self.assertHasAttr(parser_add, "subnamespace")
61916191

6192-
# 'subnamespace' attribute is a string
6193-
self.assertIsInstance(parser_add.subnamespace, str)
6192+
# 'subnamespace' attribute is a bool
6193+
self.assertIsInstance(parser_add.subnamespace, bool)
61946194

61956195
# check nesting of Namspaces works
61966196
args = parser.parse_args(["add"])
@@ -6207,36 +6207,30 @@ def test_single_subnamespace(self):
62076207
self.assertEqual(args, argparse.Namespace(action=None))
62086208

62096209
def test_double_subnamespace(self):
6210-
def add_address_args_inet(parser):
6211-
parser.add_argument("address")
6212-
parser.add_argument("port", type=int)
6213-
parser.add_argument("--use-proxy", action="store_true")
6210+
inet = argparse.ArgumentParser(add_help=False)
6211+
inet.add_argument("address")
6212+
inet.add_argument("port", type=int)
6213+
inet.add_argument("--use-proxy", action="store_true")
62146214

6215-
def add_address_args_unix(parser):
6216-
parser.add_argument("path")
6215+
unix = argparse.ArgumentParser(add_help=False)
6216+
unix.add_argument("path")
62176217

6218-
parser = argparse.ArgumentParser()
6218+
parser = argparse.ArgumentParser(prog="my-socat")
62196219
parser.add_argument("--key-file")
62206220
action = parser.add_subparsers(required=True, dest="action")
62216221

6222-
parser_bind = action.add_parser("bind", subnamespace="bind")
6222+
parser_bind = action.add_parser("bind", subnamespace=True)
62236223
parser_bind.add_argument("--fork", action="store_true")
62246224
bind_family = parser_bind.add_subparsers(required=True, dest="family")
62256225

6226-
parser_bind_inet = bind_family.add_parser("inet", subnamespace="inet")
6227-
add_address_args_inet(parser_bind_inet)
6228-
6229-
parser_bind_unix = bind_family.add_parser("unix", subnamespace="unix")
6230-
add_address_args_unix(parser_bind_unix)
6226+
parser_bind_inet = bind_family.add_parser("inet", subnamespace=True, parents=[inet])
6227+
parser_bind_unix = bind_family.add_parser("unix", subnamespace=True, parents=[unix])
62316228

6232-
parser_connect = action.add_parser("connect", subnamespace="connect")
6229+
parser_connect = action.add_parser("connect", subnamespace=True)
62336230
connect_family = parser_connect.add_subparsers(required=True, dest="family")
62346231

6235-
parser_connect_inet = connect_family.add_parser("inet", subnamespace="inet")
6236-
add_address_args_inet(parser_connect_inet)
6237-
6238-
parser_connect_unix = connect_family.add_parser("unix", subnamespace="unix")
6239-
add_address_args_unix(parser_connect_unix)
6232+
parser_connect_inet = connect_family.add_parser("inet", subnamespace=True, parents=[inet])
6233+
parser_connect_unix = connect_family.add_parser("unix", subnamespace=True, parents=[unix])
62406234

62416235
# check doubly-nested Namespaces work
62426236
# we assume if this test passes that we don't need to write
@@ -6268,13 +6262,13 @@ def test_mixed_some_subnamespace_some_not(self):
62686262
parser = argparse.ArgumentParser()
62696263
action = parser.add_subparsers(required=True, dest="action")
62706264

6271-
parser_add = action.add_parser("add", subnamespace="add")
6265+
parser_add = action.add_parser("add", subnamespace=True)
62726266
spec = parser_add.add_subparsers(required=True, dest="spec")
62736267

6274-
parser_add_country = spec.add_parser("country", subnamespace=None)
6268+
parser_add_country = spec.add_parser("country", subnamespace=False)
62756269
parser_add_country.add_argument("country_name")
62766270

6277-
parser_add_color = spec.add_parser("color", subnamespace="color")
6271+
parser_add_color = spec.add_parser("color", subnamespace=True)
62786272
parser_add_color.add_argument("name")
62796273

62806274
# test that non-subnamespace parser arguments get parented to
@@ -6313,21 +6307,21 @@ def test_exotic_subnamespace_names(self):
63136307

63146308
choice = parser.add_subparsers(required=True, dest="choice")
63156309

6316-
parser_0 = choice.add_parser("0", subnamespace="0",
6310+
parser_0 = choice.add_parser("0", subnamespace=True,
63176311
help="number 0 menu item")
63186312

6319-
parser_1 = choice.add_parser("1", subnamespace="1",
6313+
parser_1 = choice.add_parser("1", subnamespace=True,
63206314
help="number 1 menu item")
63216315

6322-
parser_True = choice.add_parser("True", subnamespace="True",
6316+
parser_True = choice.add_parser("True", subnamespace=True,
63236317
help="limited edition 'True' meal")
63246318
parser_True.add_argument("--deluxe", "-d", action="store_true")
63256319

63266320
parser_double_cheeseburger = choice.add_parser("double-cheeseburger",
6327-
subnamespace="double-cheeseburger")
6321+
subnamespace=True)
63286322

63296323
parser_chicken_nuggets = choice.add_parser("chicken-nuggets",
6330-
subnamespace="chicken-nuggets")
6324+
subnamespace=True)
63316325
parser_chicken_nuggets.add_argument("-f", help="with fries",
63326326
action="store_true")
63336327

0 commit comments

Comments
 (0)