9

I want to create an optional argument, which will be the '-- ' (double dash and a space) and get everything after it as its value. The problem is that some other optional arguments might appear after '-- '. I don't want these to be parsed as optional arguments, but as the values of '-- '. For example:

python prog1 --foo 1 --bar 2

Here foo and bar are optional arguments with values 1 and 2 respectively

python prog1 --foo 1 --bar 2 -- --foo 4 --bar 14

Here I want foo and bar that come before '-- ' to be parsed as optional arguments. But I want '--foo 4 --bar 14' to be parsed as the value of the optional argument '-- '. I would like to do this without renaming the parameters that come after '-- ' to foo2 and bar2, if possible.

So is this possible? And how could this be implemented?

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
George Tseres
  • 498
  • 6
  • 19
  • 1
    Why do you want to do this? It's a complete antipattern, and will likely just confuse people (/ your future self). – will Sep 16 '14 at 15:19
  • 8
    @will -- This isn't a complete anti-pattern. This is actually a relatively common way to specify arguments that will be passed to a slave program (maybe via `subprocess.Popen`). – mgilson Sep 16 '14 at 15:21
  • @mgilson So i just read about this. That's news to me. – will Sep 16 '14 at 15:25
  • 1
    In fact, `argparse` already supports the common convention of using `--` to signal the end of option parsing. – chepner Sep 16 '14 at 15:41
  • `--` is commonly used on Unix-like OSes to separate arguments from raw input (often the raw input represents the arguments to be passed to some _other_ command, as in the case of `xargs`). See http://unix.stackexchange.com/q/11376/4322. – Vicky Chijwani Feb 10 '17 at 13:29

2 Answers2

17

argparse already supports -- as an end-of-options terminator. The only difference is that the default handling treats the following arguments individually. Suppose prog1 simply looks like

import argparse

p = argparse.ArgumentParser()

p.add_argument('--foo')
p.add_argument('--bar')
p.add_argument('remaining', nargs=argparse.REMAINDER)

print p.parse_args()

Then the command

% python prog1 --foo 1 --bar 2 -- --foo 4 --bar 14

produces

Namespace(bar='2', foo='1', remaining=['--', '--foo', '4', '--bar', '14'])

(rather than Namespace(..., remaining='-- --foo 4 --bar 14')). Than can be fixed with a quick post-processing step, if necessary:

args = p.parse_args()
args.remaining = " ".join(args.remaining)
print args

Then the result is

Namespace(bar='2', foo='1', remaining='-- --foo 4 --bar 14')

Discarding the -- argument requires some additional post-processing:

args = p.parse_args()
if args.remaining[0] == "--":
    args = args[1:]
chepner
  • 497,756
  • 71
  • 530
  • 681
  • This is brilliant -- however, I can't seem to find it in the docs. The closest that I have been able to find is `argparse.REMAINDER`, but that doesn't seem to use the `--` separator... – mgilson Sep 16 '14 at 16:20
  • See the very end of [section 15.4.4.3](https://docs.python.org/2.7/library/argparse.html#arguments-containing) (link to the Python 2.7 version of the documentation). – chepner Sep 16 '14 at 16:43
  • Thanks for reminding me about `REMAINDER`; that seems like a better choice than `*` (at least more descriptive; I can't tell if there is a functional difference in this case.) – chepner Sep 16 '14 at 17:08
  • Nice. Well, you have my +1 anyway -- and I learned something new. – mgilson Sep 16 '14 at 17:14
  • 6
    I ran this code and my remaining was `remaining=['--', '--foo', '4', '--bar', '14']`. Note the `--` showed up in the remaining. – gsgx Sep 03 '15 at 05:23
  • I have the same issue as @gsingh2011 – arekolek Aug 07 '16 at 11:59
1

It looks like the docopt library can do this for you;

From the usage-pattern-format section:

"[--]". Double dash "--" is used by convention to separate positional arguments that can be mistaken for options. In order to support this convention add "[--]" to your usage patterns.

will
  • 10,260
  • 6
  • 46
  • 69