Argparse: smooth result action = 'append'

I would like to make a script that maintains a list of form arguments

./myscript --env ONE=1,TWO=2 --env THREE=3 

Here is my attempt:

 import argparse parser = argparse.ArgumentParser() parser.add_argument( '--env', type=lambda s: s.split(','), action='append', ) options = parser.parse_args() print options.env $ ./myscript --env ONE=1,TWO=2 --env THREE=3 [['ONE=1', 'TWO=2'], ['THREE=3']] 

Of course, I can fix this in post-processing:

 options.env = [x for y in options.env for x in y] 

but I wonder if there is a way to get a flattened list directly from argparse, so I don’t need to maintain a list of “things I need to smooth out later” in my head when I add new options for the program.

The same question applies if I have to use nargs='*' instead of type=lambda...

 import argparse parser = argparse.ArgumentParser() parser.add_argument( '--env', nargs='+', action='append', ) options = parser.parse_args() print options.env $ ./myscript --env ONE=1 TWO=2 --env THREE=3 [['ONE=1', 'TWO=2'], ['THREE=3']] 
+5
source share
2 answers

Unfortunately, by default ArgumentParser does not have an extend action. But it is not so difficult to register:

 import argparse class ExtendAction(argparse.Action): def __call__(self, parser, namespace, values, option_string=None): items = getattr(namespace, self.dest) or [] items.extend(values) setattr(namespace, self.dest, items) parser = argparse.ArgumentParser() parser.register('action', 'extend', ExtendAction) parser.add_argument('--env', nargs='+', action='extend') args = parser.parse_args() print(args) 

Demo:

 $ python /tmp/args.py --env one two --env three Namespace(env=['one', 'two', 'three']) 

lambda that you have in your example is somewhat outside of the intended use case of type kwarg. Therefore, I would recommend instead dividing into spaces, because it will be painful to correctly handle the case when , in fact, is in the data. If you split up into space, you get this functionality for free:

 $ python /tmp/args.py --env one "hello world" two --env three Namespace(env=['one', 'hello world', 'two', 'three']) 
+6
source

It requires an extend action class for ( http://bugs.python.org/issue23378 ), but since it's easy to add your own, I think that a function will ever be added.

The general Python idiom uses chain to align the list:

 In [36]: p=[['x'], ['y']] In [37]: from itertools import chain In [38]: chain(*p) Out[38]: <itertools.chain at 0xb17afb2c> In [39]: list(chain(*p)) Out[39]: ['x', 'y'] 

Your understanding of the list is equivalent to:

 In [40]: [x for y in p for x in y] Out[40]: ['x', 'y'] 

At http://bugs.python.org/issue16399#msg277919 I offer another option - a custom default value for the append argument.

 class MyList(list): def append(self,arg): if isinstance(arg,list): self.extend(arg) else: super(MyList, self).append(arg) parser = argparse.ArgumentParser() a = parser.add_argument('-f', action='append', nargs='*',default=MyList([])) args = parser.parse_args('-f 1 2 3 -f 4 5'.split()) 

which produces

 Namespace(f=['1', '2', '3', '4', '5']) 

You will need to use your own opinion about what is appropriate in the production code.

+1
source

Source: https://habr.com/ru/post/1261292/


All Articles