Argparse: how to accept any number of optional arguments (starting with `-` or` --`)

I am trying to create a command line tool (let’s call it β€œX”) that wraps another tool (let's call it β€œY”).

I handle some cases on purpose and add some parameters for myself, but I want to redirect what I don’t want to handle the Y tool.

So far, I have managed to redirect arguments that don't come with dashes, for example XY option1 option2 option3 will just call Y option1 option2 option3 . I did this by adding a subparameter Y to it and the argument any

Here's the code (x.py):

 main_parser = argparse.ArgumentParser() subparsers = main_parser.add_subparsers(dest="parser_name") y_subparser = subparsers.add_parser('y') y_options = y_subparser.add_argument('any', nargs='*') 

Then in my handler code, I do this:

 args = main_parser.parse_args() if args.parser_name == 'y': command_string = ' '.join(['y'] + sys.argv[2:]) os.system(command_string) 

When I call python x.py y asdf zxcv qwer , it works.

When I call python x.py y asdf zxcv qwer -option , I get the x.py: error: unrecognized arguments: -option

I understand that if the material just gets too complicated with argparse, I can always revert to using sys.argv , but if you know this is doable, share it.

I am also looking at argparse code, which is a little tight, and where it seems that ArgumentParser._parse_known_args does everything (300 lines). But before I go deeper, I thought that maybe someone knows how to do this - if not, I will publish my findings here if someone has another problem.

+5
source share
2 answers

From the argparse document, you can use argparse.REMAINDER :

 >>> parser = argparse.ArgumentParser(prog='PROG') >>> parser.add_argument('--foo') >>> parser.add_argument('command') >>> parser.add_argument('args', nargs=argparse.REMAINDER) >>> print(parser.parse_args('--foo B cmd --arg1 XX ZZ'.split())) Namespace(args=['--arg1', 'XX', 'ZZ'], command='cmd', foo='B') 

This work even with a dotted line in the subcommand arguments

 >>> parser = argparse.ArgumentParser(prog='PROG') >>> parser.add_argument('--foo') >>> parser.add_argument('command') >>> parser.add_argument('args', nargs=argparse.REMAINDER) >>> print(parser.parse_args('--foo B cmd --arg1 XX ZZ --foobar'.split())) Namespace(args=['--arg1', 'XX', 'ZZ', '--foobar'], command='cmd', foo='B') 
+5
source

I found this workaround:

Instead of simple main_parser.parse_args() I do this

 def remove_dashes_from_args(args): return [arg.replace('-','#$%#$') for arg in args] main_parser.parse_args(replace_dashes_from_args(sys.argv[1:])) 

And then when invoking the Y command, I didn't need to change anything, because I already used sys.argv:

 if arg.parser_name == 'y': os.system(' '.join(['y'] + sys.argv[2:])) 
0
source

All Articles