This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author paul.j3
Recipients paul.j3, py.user
Date 2015-06-18.20:49:41
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1434660582.99.0.834783842303.issue24419@psf.upfronthosting.co.za>
In-reply-to
Content
(Important correction at the end of this post)

The test that you are complaining about occurs at the start of the 'add_argument' method:

    def add_argument(self, *args, **kwargs):
        """
        add_argument(dest, ..., name=value, ...)
        add_argument(option_string, option_string, ..., name=value, ...)
        """

        # if no positional args are supplied or only one is supplied and
        # it doesn't look like an option string, parse a positional
        # argument
        chars = self.prefix_chars
        if not args or len(args) == 1 and args[0][0] not in chars:
            if args and 'dest' in kwargs:
                raise ValueError('dest supplied twice for positional argument')
            kwargs = self._get_positional_kwargs(*args, **kwargs)

        # otherwise, we're adding an optional argument
        else:
            kwargs = self._get_optional_kwargs(*args, **kwargs)
        ...

and the 2 methods it calls:

    def _get_positional_kwargs(self, dest, **kwargs):
        # code to deduce the 'required' parameter ...
        # return the keyword arguments with no option strings
        return dict(kwargs, dest=dest, option_strings=[])

    def _get_optional_kwargs(self, *args, **kwargs):
        # determine short and long option strings
        ....
        # infer destination, '--foo-bar' -> 'foo_bar' and '-x' -> 'x'
        dest = kwargs.pop('dest', None)
        if dest is None:
            if long_option_strings:
                dest_option_string = long_option_strings[0]
            else:
                dest_option_string = option_strings[0]
            dest = dest_option_string.lstrip(self.prefix_chars)
            if not dest:
                msg = _('dest= is required for options like %r')
                raise ValueError(msg % option_string)
            dest = dest.replace('-', '_')

        # return the updated keyword arguments
        return dict(kwargs, dest=dest, option_strings=option_strings)

At the 'add_argument' stage, a big difference between positionals and optionals is in how 'dest' is deduced.  Note the doc string.

During parsing, positionals are distinguished from optionals by the 'option_strings' attribute (empty or not).  'dest' is not used during parsing, except by the Action '__call__'.

-------------------------

I just thought of another way around this constraint - set 'dest' after the action is created:

    p=argparse.ArgumentParser()
    a1=p.add_argument('foo',action='append')
    a2=p.add_argument('bar',action='append')
    a1.dest='x'
    a2.dest='x'
    args=p.parse_args(['one','two'])

produces

    Namespace(x=['one', 'two'])

This works because after the action has been created, no one checks whether the 'dest' value is duplicated or even looks pretty (except when trying to format it for the help.

You could also write a custom Action class, one that mangles the 'dest' to your heart's delight.  The primary use of 'self.dest' is in the expression:

    setattr(namespace, self.dest, items)

you could replace this line in the Action '__call__' with

    setattr(namespace, 'secret#dest', items)

-----------------

I was mistaken on one thing - you can reuse positional 'names':

     a1=p.add_argument('foo',action='append')
     a2=p.add_argument('foo',action='append',type=int)
     p.parse_args(['a','3'])

produces:

     Namespace(foo=['a', 3])

There is a 'name' conflict handler, but it only pays attention to the option strings (flags for optionals).  You can't have two arguments using '-f' or '--foo'.  But you can have 2 or more positionals with the same 'dest'.  You just have to set the dest the right way.

This last point renders the whole issue moot.  But I'll leave it at the end to reflect my train of thought.
History
Date User Action Args
2015-06-18 20:49:43paul.j3setrecipients: + paul.j3, py.user
2015-06-18 20:49:42paul.j3setmessageid: <1434660582.99.0.834783842303.issue24419@psf.upfronthosting.co.za>
2015-06-18 20:49:42paul.j3linkissue24419 messages
2015-06-18 20:49:41paul.j3create