``dest`` parameter behaves the same for positional and optional arguments. >>> parser = ArgumentParser() >>> parser.add_argument('foo-bar') >>> args = parser.parse_args(['f']) >>> print(args.foo_bar) f Also preserves backwards compatibility for pre-3.4 attribute access: >>> print(args.foo_bar == getattr(args, 'foo-bar')) True diff --git a/Lib/argparse.py b/Lib/argparse.py --- a/Lib/argparse.py +++ b/Lib/argparse.py @@ -1191,7 +1191,17 @@ class Namespace(_AttributeHolder): return not (self == other) def __contains__(self, key): - return key in self.__dict__ + return key in self.__dict__ or key.replace('-', '_') in self.__dict__ + + def __getattr__(self, name): + # Compatibility for people doing getattr(args, 'foo-bar') instead of + # args.foo_bar. This might lead to some false positives, but this + # is unlikely. + try: + return self.__dict__[name.replace('-', '_')] + except KeyError: + msg = "'%s' object has no attribute '%s'" + raise AttributeError(msg % (type(self).__name__, name)) class _ActionsContainer(object): @@ -1411,6 +1421,9 @@ class _ActionsContainer(object): if kwargs.get('nargs') == ZERO_OR_MORE and 'default' not in kwargs: kwargs['required'] = True + # make dest attribute-accessible, 'foo-bar' -> 'foo_bar' + dest = dest.replace('-', '_') + # return the keyword arguments with no option strings return dict(kwargs, dest=dest, option_strings=[]) diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -1116,6 +1116,27 @@ class TestPositionalsNargsOptionalOneOrM ] +class TestPositionalDest(ParserTestCase): + """Tests setting destination""" + + argument_signatures = [Sig('foo-bar')] + failures = [] + successes = [ + ('f', NS(foo_bar='f')), + ] + + def test_compatibility(self): + parser = ErrorRaisingArgumentParser(prog='PROG', + description='main description') + parser.add_argument('foo-bar') + args = parser.parse_args(['f']) + self.assertEqual(args.foo_bar, 'f') + self.assertEqual(getattr(args, 'foo-bar'), 'f') + self.assertRaisesRegexp(AttributeError, + "'Namespace' object has no attribute 'a-b'", + getattr, args, 'a-b') + + class TestPositionalsChoicesString(ParserTestCase): """Test a set of single-character choices"""