Have argparse collect, but not respond to, flags

2019-08-09 23:22发布

I have a script which takes in some arguments, uses some of those argument to choose a script to run, and passes the rest of the arguments to that script. So it looks something like this:

parser = ArgumentParser()
parser.add_argument('script', choices['a', 'b'])
parser.add_argument('rest_args', nargs='*')
args = parser.parse_args()
if args.script == 'a':
    subprocess.call('python a.py %s' % ' '.join(args.rest_args))
else:
    subprocess.call('python b.py %s' % ' '.join(args.rest_args))

This works fine, unless I want to pass in arguments that start with -. For example, if I called python my_script.py a --foo, I'd get an error unrecognized arguments, when really I want to just have it run python a.py --foo (i.e. just pass the --foo along to the subprocess).

Is there a way to get around this with argparse?

3条回答
地球回转人心会变
2楼-- · 2019-08-09 23:55

I discovered the function parse_known_args, which provides a solution to this, albeit perhaps not ideal.

parser = ArgumentParser()
parser.add_argument('script', choices['a', 'b'])
args, rest_args = parser.parse_known_args()
if args.script == 'a':
    subprocess.call('python a.py %s' % ' '.join(rest_args))
else:
    subprocess.call('python b.py %s' % ' '.join(rest_args))

What this does is use parse_known_args to parse what's known, and collect the rest in a list. Then those remaining arguments can be passed to the subprocess as desired.

查看更多
放荡不羁爱自由
3楼-- · 2019-08-10 00:07

Perhaps you are looking for parse_known_args. It will parse all the options it recognizes, and returns all the unrecognized arguments in unknown:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('script', choices=['a', 'b'])

args, unknown = parser.parse_known_args(['a', '--foo'])

print(args)
# Namespace(script='a')
print(unknown)
# ['--foo']
查看更多
不美不萌又怎样
4楼-- · 2019-08-10 00:12

The '*' works if you include '--' in the argument list:

1502:~/mypy$ python2.7 stack26264831.py a -- --foo bar
Namespace(rest_args=['--foo', 'bar'], script='a')

An alternative is to use REMAINDER ('...') instead of '*':

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('script', choices=['a', 'b'])
parser.add_argument('rest_args', nargs=argparse.REMAINDER)
args = parser.parse_args()
print args

1500:~/mypy$ python2.7 stack26264831.py a --foo bar
Namespace(rest_args=['--foo', 'bar'], script='a')

Both the '--' and REMAINDER mean: 'treat what follows as positional arguments'.

parse_known_args also works, though its logic is different - 'just return a list of the strings you didn't recognize'. parse_args calls parse_known_args and raises an error is the rest value is not empty.


But don't use REMAINDER as the first positional argument: Using argparse.REMAINDER at beginning of parser / sub parser

查看更多
登录 后发表回答