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()