argparse action or type for comma-separated list argparse action or type for comma-separated list python-3.x python-3.x

argparse action or type for comma-separated list


The simplest solution is to consider your argument as a string and split.

#!/usr/bin/env python3import argparseparser = argparse.ArgumentParser()parser.add_argument("--myarg", type=str)d = vars(parser.parse_args())if "myarg" in d.keys():    d["myarg"] = [s.strip() for s in d["myarg"].split(",")]print(d)

Result:

$ ./toto.py --myarg=abcd,e,fg{'myarg': ['abcd', 'e', 'fg']}$ ./toto.py --myarg="abcd, e, fg"{'myarg': ['abcd', 'e', 'fg']}


I find your first solution to be the right one. The reason is that it allows you to better handle defaults:

names: List[str] = ['Jane', 'Dave', 'John']parser = argparse.ArumentParser()parser.add_argument('--names', default=names, action=SplitArgs)args = parser.parse_args()names = args.names

This doesn't work with list_str because the default would have to be a string.


Your custom action is the closest way to how it is done internally for other argument types. IMHO there should be a _StoreCommaSeperatedAction added to argparse in the stdlib since it is a somewhat common and useful argument type,

It can be used with an added default as well.

Here is an example without using an action (no SplitArgs class):

class Test:    def __init__(self):        self._names: List[str] = ["Jane", "Dave", "John"]    @property    def names(self):        return self._names    @names.setter    def names(self, value):        self._names = [name.strip() for name in value.split(",")]test_object = Test()parser = ArgumentParser()parser.add_argument(    "-n",    "--names",    dest="names",    default=",".join(test_object.names),  # Joining the default here is important.    help="a comma separated list of names as an argument",)print(test_object.names)parser.parse_args(namespace=test_object)print(test_object.names)

Here is another example using SplitArgs class inside a class completely

    """MyClassDemonstrates how to split and use a comma separated argument in a class with defaults"""import sysfrom typing import Listfrom argparse import ArgumentParser, Actionclass SplitArgs(Action):    def __call__(self, parser, namespace, values, option_string=None):        # Be sure to strip, maybe they have spaces where they don't belong and wrapped the arg value in quotes        setattr(namespace, self.dest, [value.strip() for value in values.split(",")])class MyClass:    def __init__(self):        self.names: List[str] = ["Jane", "Dave", "John"]        self.parser = ArgumentParser(description=__doc__)        self.parser.add_argument(            "-n",            "--names",            dest="names",            default=",".join(self.names),  # Joining the default here is important.            action=SplitArgs,            help="a comma separated list of names as an argument",        )        self.parser.parse_args(namespace=self)if __name__ == "__main__":    print(sys.argv)    my_class = MyClass()    print(my_class.names)    sys.argv = [sys.argv[0], "--names", "miigotu, sickchill,github"]    my_class = MyClass()    print(my_class.names)

And here is how to do it in a function based situation, with a default included

class SplitArgs(Action):    def __call__(self, parser, namespace, values, option_string=None):        # Be sure to strip, maybe they have spaces where they don't belong and wrapped the arg value in quotes        setattr(namespace, self.dest, [value.strip() for value in values.split(",")])names: List[str] = ["Jane", "Dave", "John"]parser = ArgumentParser(description=__doc__)parser.add_argument(    "-n",    "--names",    dest="names",    default=",".join(names),  # Joining the default here is important.    action=SplitArgs,    help="a comma separated list of names as an argument",)parser.parse_args()