I can't seem to find any support for multiple arguments of different types. All I could find is this issue request (https://bugs.python.org/issue38217).
Here it was recommended to do a type check in the 'post parsing' code, e.g. have both 5 and fileName be strings, and simply convert the 5 to an int as required. You could specify the argument simply as such, by taking exactly 2 arguments.:
parser.add_argument('--rating',
nargs=2,
type=str,
help="1st arg: rating (1-5), 2nd arg: file name.")
Then, you can unpack the values from the list (as nargs will bundle values into a list).
rating = int(args.rating[0])
file_name = args.rating[1]
Hope this helps!
Answer from You_Donut on Stack OverflowI can't seem to find any support for multiple arguments of different types. All I could find is this issue request (https://bugs.python.org/issue38217).
Here it was recommended to do a type check in the 'post parsing' code, e.g. have both 5 and fileName be strings, and simply convert the 5 to an int as required. You could specify the argument simply as such, by taking exactly 2 arguments.:
parser.add_argument('--rating',
nargs=2,
type=str,
help="1st arg: rating (1-5), 2nd arg: file name.")
Then, you can unpack the values from the list (as nargs will bundle values into a list).
rating = int(args.rating[0])
file_name = args.rating[1]
Hope this helps!
I'm sure that you've seen this tutorial: https://docs.python.org/2/howto/argparse.html
But even that is a bit obtuse. So here is a shortcut by way of example:
import argparse
def get_command_line_arguments():
parser = argparse.ArgumentParser(description='Parse --foo and --bar from the command line')
parser.add_argument('--foo', default=0, type=int, choices=[0, 1, 2], help="gives the foo argument")
parser.add_argument('--bar', default=1.0, type=float, help="the bar floating scaler")
parser.add_argument('--zoo', default="", help="zoo is an optional string")
args = parser.parse_args()
return args
def main():
args = get_command_line_arguments()
foo = args.foo
bar = args.bar
zoo = args.zoo
And that is all there is to it - at least for a get started example.
argparse: type vs action
Suggestions for retaining type hinting when using argparse without telling my IDE to look the other way
command line interface - Adding options with different argument types using Python's argparse module - Stack Overflow
python - Can one commandline argument's `type` be dependent on the value of another argument with argparse? - Stack Overflow
Videos
I have just started learning Python, coming from more low level language's but have some shell scripting experiences.
I wanted to write a tool with command line arguments, and quickly found the argparse module in Python's documentation. I am prone to object oriented programming and quickly ended up in a situation with a mess of post-processing string arguments into different class instances.
My mediocre Google-Fu found multiple ways of cleanly support custom type parsing, namely using the add_argument's type or action parameters. However, no site really explained their purposes; just a bunch of examples, using either mechanism. I did not feel I got a good understanding from the docs either.
What is the purpose of these two mechanics?
I read somewhere that arguments are passed through the callable type parameter and then forwarded to the callable action parameter.
Is the type mechanism supposed to parse a string to another type, and action supposed to use that newly created type instance to do whatever magic you want to happen?
» pip install typed-argparse
So let's say we would like to make our program to have a command-line interface. By default, parsed arguments coming from the parse_args() method in the argparse module has a Namespace typing. Which doesn't help with auto-completion nor with type hinting argument types.
Alright, so I type hint the exact namespace that I expect:
import argparse
class MyProgramArgs(argparse.Namespace):
somearg: str
somenum: int
def process_argv():
parser = argparse.ArgumentParser()
parser.add_argument('--somearg', default='defaultval')
parser.add_argument('--somenum', type=int)
parsed: MyProgramArgs = parser.parse_args() # type: ignore
the_arg = parsed.someargAnd for all purposes, this works. I get auto-completion suggestion and appropriate typing hinting. But I had to explicitly tell my code editor to look the other way in this line:
parsed: MyProgramArgs = parser.parse_args() # type: ignore
Could the same outcome have been managed without this hack (and without using 3rd party libraries)?
From the docs:
Different values of
nargsmay cause the metavar to be used multiple times. Providing a tuple tometavarspecifies a different display for each of the arguments:>>> parser = argparse.ArgumentParser(prog='PROG') >>> parser.add_argument('-x', nargs=2) >>> parser.add_argument('--foo', nargs=2, metavar=('bar', 'baz')) >>> parser.print_help() usage: PROG [-h] [-x X X] [--foo bar baz] options: -h, --help show this help message and exit -x X X --foo bar baz
So specify metavar=('<str>', '<int>', '<int>').
As others wrote, giving a tuple metavar that matches the nargs gives the desired usage/help display:
In [20]: parser=argparse.ArgumentParser();
...: parser.add_argument(
...: '--opt',
...: nargs=3,
...: metavar=('<str>', '<int>', '<int>'),
...: help="...",
...: );
In [21]: parser.print_help()
usage: ipykernel_launcher.py [-h] [--opt <str> <int> <int>]
options:
-h, --help show this help message and exit
--opt <str> <int> <int>
...
In [22]: parser.parse_args('--opt foo 2 3'.split())
Out[22]: Namespace(opt=['foo', '2', '3'])
An alternative would let it display as '--opt OPT OPT OPT' and give more details in the help string.
parents is just a way of copying the add_argument of one parser to a new one. It does not add any functionality.
The other task is testing and converting those three strings.
The type function only sees one argument string at a time, so can't (readily) distinguish between the string and int arguments.
The action __call__ method does get all (3) strings. So a custom Action class could test and convert the 2nd and 3rd, before saving them to the namespace. The code to do this would look a lot like the code you'd write to process args.opt after parsing. You'll have to look at the argparse.py code to copy and modify a existing Action subclass.
But I think a custom 'Action' class is worth the work only if you have several arguments with this type of input. Otherwise, doing the post-argparse processing will be simplest.
You don't get extra credit for doing all the input handling within the parser.
In [27]: args=parser.parse_args('--opt foo bar 3'.split())
In [29]: try:
...: opts = [args.opt[0], int(args.opt[1]), int(args.opt[2])]
...: except ValueError:
...: parser.error('opt arguments should be str,int,int')
...:
usage: ipykernel_launcher.py [-h] [--opt <str> <int> <int>]
ipykernel_launcher.py: error: opt arguments should be str,int,int
....