python : argparse boolean arguments via command line [duplicate] - python

This question already has answers here:
Parsing boolean values with argparse
(26 answers)
Closed 4 years ago.
I would like to pass in arguments to this sampleCode.py file.
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--num_epochs', '-n', default=2, type=int)
parser.add_argument('--directory', default='/some/string/')
parser.add_argument('--exclude_hydro', default=False, action='store_true')
args = parser.parse_args()
print(args.num_epochs) # 2
print(args.exclude_hydro) # False
The following commands work for int and string arguments, but not for boolean.
$python3 sampleCode.py -n3 #args.num_epochs=3
$python3 sampleCode.py --num_epochs 3 #args.num_epochs=3
$python3 sampleCode.py --directory /new/string #args.directory = /new/string
$python3 sampleCode.py --exclude_hydro True #error
How can I pass in boolean arguments? I have tried type=bool as a parameter for .add_argument() but that doesn't work.

parser.add_argument('--exclude-hydro', const=True) should work.
Note that you should also be defining help="<help_text>" so that when a user calls the application without any arguments, help text is displayed describing the purpose of each argument:
parser.add_argument('--exclude-hydro', const=True, help="<help_text>")

Related

Python argv "False, false' are recognized as True [duplicate]

This question already has answers here:
Parsing boolean values with argparse
(26 answers)
Closed 1 year ago.
I ran my program using the below line
python ***.py --if_normalize False
but my program recognizes if_normalize variable to be True
def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument("--work-path", required=True, type=str)
parser.add_argument("--seed", default=42, type=int, help="Random seed")
parser.add_argument("--batch-size", default=128, type=int)
parser.add_argument("--attack", default='no', type=str)
parser.add_argument("--gpu",default=2, type=int)
parser.add_argument('--if_normalize', default=False, type=bool)
return parser.parse_args()
args = parse_args()
print(args)
and it prints if_normalize is True
I passed 'False, false, -1' but still it recognizes as True
Because it just runs the input through bool, and bool('False') is True, because any non-empty string is truthy.
You'd usually do this by using a flag instead:
parser.add_argument('--if-normalize', action='store_true')
The store_true action implies a default of False, and sets it to True if the flag is present. I.e. you'd use it like:
$ python ***.py --if-normalize
You should change to the following:
parser.add_argument('--if_normalize', action='store_true')
This will set the argument if_normalize to True when you add it to the command line arguments when calling to your script.
You can refer to the docs and see exactly what it does.
https://docs.python.org/3/library/argparse.html#type
The bool() function is not recommended as a type converter. All it does is convert empty strings to False and non-empty strings to True. This is usually not what is desired.
This type section was recently expanded in an attempt to remove some common misunderstandings, including this misuse of the bool (a builtin) function.
You could write your own function that parses a whole slew of 'yes/no' words. Developers have declined to do so because there are too many alternatives, especially if non-English possibilities are included.
The store_true Action is enough for most purposes.

Add an argument that starts with "h"

With this code, I can't seem to add "--height" as argument because Python is confused with the "-h / --help" default option. I've tried to add add_help=False when creating the object but I still get the error main.py: error: the following arguments are required: height
import argparse
parser = argparse.ArgumentParser(description='my description')
parser.add_argument('height', type=int, nargs=1)
args = parser.parse_args()
You created a positional argument. The way argparse works is that when you define an argument without any leading - or -- it will consider it positional, so you have to call the script like python yourscript.py the_height.
If you want to call it like python myscript.py --height 222 then you must do
parser.add_argument("--height", action="store")
args_namespace = parser.parse_args()
print(args_namespace.height)

How to pass and parse a list of strings from command line with argparse.ArgumentParser in Python?

I want to pass a list of names into my program written in Python from console. For instance, I would like to use a way similar to this (I know it shouldn't work because of bash):
$ python myprog.py -n name1 name2
So, I tried this code:
# myprog.py
from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument('-n', '--names-list', default=[])
args = parser.parse_args()
print(args.names_list) # I need ['name1', 'name2'] here
That led to the error:
usage: myprog.py [-h] [-n NAMES_LIST]
myprog.py: error: unrecognized arguments: name2
I know I could pass the names with quotes "name1 name2" and split it in my code args.names_list.split(). But I'm curious, is there a better way to pass the list of strings via argparse module.
Any ideas would be appreciated.
Thanks!
You need to define --names-list to take an arbitrary number of arguments.
parser.add_argument('-n', '--names-list', nargs='+', default=[])
Note that options with arbitrary number of arguments don't typically play well with positional arguments, though:
# Is this 4 arguments to -n, or
# 3 arguments and a single positional argument, or ...
myprog.py -n a b c d
You need to use nargs:
parser.add_argument('-n', '--names-list', nargs="*")
https://docs.python.org/3/library/argparse.html#nargs
parser.add_argument('-n', '--names-list', default=[], nargs='+')

Understanding argument parsing with argparse in Python

I am now starting exploring Python, and was testing how arguments can be passed to a script with "argparse".
The way I wrote a sample script was as following, where arguments passed through flags -i and -o are compulsory and flag -u is optional:
#!/usr/bin/python
import sys
import argparse
## set usage options and define arguments
usage = "usage: %prog [options]"
parser = argparse.ArgumentParser(usage)
parser.add_argument("-i", action="store", dest="input", help="input file")
parser.add_argument("-o", action="store", dest="output", help="output file")
parser.add_argument("-u", action="store_true", dest="isunfolded", default=False, help="optional flag")
args = parser.parse_args()
print len(sys.argv)
if len(sys.argv) < 2:
# parser.print_help()
print 'Incorrect number of params'
exit()
else:
print "Correct number of params: ", len(sys.argv)
Running this script:
> ./test-args.py -i a -o b
prints:
5
Correct number of params: 5
I understand the printing statement in the if conditional (5 is higher than 2), however, after reading the argparse documentation (https://docs.python.org/3/library/argparse.html) I still don't quite understand why -i and -o flags are counted as arguments. This behaviour seems to be quite different from e.g. perl Getopt::Std, which I'm more used to.
So, the question is what is the best way of parsing arguments in Python and to evaluate the presence of mandatory arguments (without using required=True)
It gives you 5 because sys.argv contains the raw input passed to python as arguments (the script name and 4 arguments).
You can see argparse as an abstraction for this, so once you use it, you can forget about sys.argv. In most cases it is better not to mix these two methods.
argparse its a nice way to handle arguments, I don't quite get why you don't want to use the required option when that's exactly the way to go. Another alternative is to parse the sys.argv yourself (regex maybe?) and drop argparse altogether.
There's a Python getopt which probably is similar to the Perl one (assuming both are modelled after the C/Unix version).
https://docs.python.org/2/library/getopt.html
In your code, sys.argv is a list of strings from the command line (as interpreted by the shell and interpreter). It is the raw input for any of the parsers ('getopt', 'optparse', 'argparse'). And it is available for your parsing as well. When learning it is a good idea to include a
print sys.argv
line. parser.parse_args() uses this list. sys.argv[0] is used as prog attribute (in the default usage), while sys.argv[1:] (the rest) is parsed according to the rules you define with add_argument. For testing I often like to use parse_args with a custom list of strings, e.g.
print parser.parse_args(['-i', 'input', '-o', 'output', '-u'])
With your definition I'd expect to see something like:
Namespace(input='input', output='output', isunfolded=True)
The parser returns an object (type argparse.Namespace), which has attributes defined by your arguments. Values are usually accessed with expressions like args.input, args.isunfolded. The docs also show how you easily express this as a dictionary.
By long standing UNIX conventions, arguments flagged by strings like '-i' are options, that is they are optional. argparse generalizes this concept by letting you specify a required=True parameter.
Other arguments are positionals. They are interpreted according to their order. And as such they are normally required. What argparse adds is the ability to define those positionals, such as type, nargs, etc. With nargs='?' they are optional. Many of the nargs values are similar to regular expression characters (e.g. +?*). In fact argparse uses a form a regular expression parsing to allocate strings among arguments.
I'd refine your arguments thus (taking advantage of various defaults)
a1 = parser.add_argument("-i", "--input", help="input file") # 'store' is the default
a2 = parser.add_argument("-o", "--output",help="output file") # use the --output as dest
a3 = parser.add_argument("-u", "--isunfolded", action="store_true", help="optional flag")
If input and output were required, I could change them to:
parser.add_argument("input", help="input file") # 'store' is the default
parser.add_argument("output",help="output file") # use the --output as dest
parser.add_argument("-u", "--isunfolded", action="store_true", help="optional flag")
Now input and output are positional arguments, as in test.py -u inputfile outputfile
By using a1 = parser... I can look at the object produced by this statement.
print a1
produces
_StoreAction(option_strings=['-i', '--input'], dest='input', nargs=None, const=None,
default=None, type=None, choices=None, help='input file', metavar=None)
This tells me that a1 is a _StoreAction object (a subclass of argparse.Action). It also displays a number (not all) of its attributes, ones that define its action. A positional, on the other hand, has values like these:
a2 = p.add_argument("output", help="output file")
_StoreAction(option_strings=[], dest='output', nargs=None, const=None,
default=None, type=None, choices=None, help='output file', metavar=None)
It may also be instructive to look at a1.required and a2.required, which are respectively False and True. required is an Action attribute that is not routinely displayed, but is, never the less accessible.
I've pulled all these test values from a parser defined in an interactive shell (Ipython). It's a great way to explore Python and modules like argparse.
After reading other related posts it seems that the best way to do this is as was suggested by #Rufflewind and inspect the args itself:
if not args.input or not args.output:
print 'Incorrect number of params'
exit()
else:
print "Correct number of params"

Python arguments not being parsed when passed

I have the following code at the minute:
parser = argparse.ArgumentParser(prog='Tempus')
ex_group = parser.add_mutually_exclusive_group(required=True)
## Miscellaneous but needed args
parser.add_argument('--mode', type=str, choices=['xml', 'text', 'term'],
dest='mode', required=True, help='export mode')
parser.add_argument('-v', '--verbose', action='store_true',
dest='verbose', help='enable verbose/debug mode')
# Input methods
ex_group.add_argument('--i', action='store_true',
dest='interactive', help='enter interactive mode')
ex_group.add_argument('--p', metavar='I', type=float,
dest='integer', help='percentage to use')
args = parser.parse_args()
However when I pass the arguments that are needed, in any order, I get the error:
Tempus: error: argument --mode is required
And this happens, even when I pass that argument. Any way to sort this?
Thank you!
EDIT: Thanks everyone, I got it working, turns out executing it without first specifying the python exe before it doesn't pass arguments.
Just another quick question: is it possible to create an argument similar to --mode in my code, but let an additional argument be passed to one of the choices?
For example, have a command such as --input which can take two arguments 'integer' and 'interactive', but I can also pass a number to the --input integer command? So the command would read: --input integer 23 for example? Is this possible?
Try
1. to print sys.argv before parsing or
2. to explicitely add arguments inside the script as in http://docs.python.org/dev/library/argparse.html#parsing-arguments
So you can exclude any confusion about which arguments argparse actually sees.

Categories

Resources