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

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.

Related

Argument's name shadows keyword in module argparse python [duplicate]

This question already has answers here:
Using a python keyword as an option in argparse
(2 answers)
Closed 14 days ago.
When I try to get argument with flag "--from" from argparse.ArgumentParser.parse_args() an error occurs. IDE says that "from" is import statement and the code is unreachable:
parser = argparse.ArgumentParser(prog='cache_wiki.py',
description='Find shortest path between links')
parser.add_argument('--from', required=True, help='page to start search with')
args = parser.parse_args()
print(args.from)
It is ok with another name:
parser = argparse.ArgumentParser(prog='cache_wiki.py',
description='Find shortest path between links')
parser.add_argument('--f', required=True, help='page to start search with')
args = parser.parse_args()
print(args.f)
but I really need to use flag "--from".
I would ignore the IDE here. True, you cannot use args.from, but that's just a syntactic limitation. You can still access the attribute using, for example, getattr(args, 'from').
You can also override the default destination name so that you can use the option --from, but set a different attribute:
...
parser.add_argument('--from',
required=True,
dest='from_',
help='page to start search with')
args = p.parse_args(['--from', 'foo'])
assert args.from_ == 'foo'

How do I set a default value for flag in argparse if the flag is given alone [duplicate]

This question already has answers here:
Argparse optional argument with different default if specified without a value
(2 answers)
Closed 3 months ago.
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-c',
'--cookies',
nargs='?',
default=5,
type=int,
)
args = parser.parse_args()
if args.cookies:
print('cookies flag is set: ' + args.cookies)
else:
print('cookies flag not set: ' + str(args.cookies))
I want it to work so that if the user gives -c then we know they want cookies, but we don't know how many cookies they want so we give them 5 by default (-c == 5 :).
If the user types -c 25 then we know they want 25 cookies.
If the user does not give a -c flag then we know they do not want cookies and cookies flag should not be set.
The way it works as above is that -c == 5 only when -c is not set by the user. But we do not want to give them cookies if they do not ask for it!
If they ask for a specific amount of cookies (ex: -c 10), then the code above works fine.
I fixed this problem by using a short custom action that checks if the flag is set and if no value is passed in I give it the default value.
This seems a bit convoluted and there must be an easier way. I've searched the argparse docs (looked at nargs, default, and const) but couldn't figure out a solution.
Any ideas? Thank you for your time.
You're looking for the const parameter, which the docs don't do a very good job of explaining.
default always sets the value, even if the flag is not provided, unless it is overridden by a user input.
const only sets the value if the flag is provided and no overriding value is provided.
The nargs section has an example of how to use the const parameter:
>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('--foo', nargs='?', const='c', default='d')
>>> parser.add_argument('bar', nargs='?', default='d')
>>> parser.parse_args(['XX', '--foo', 'YY'])
Namespace(bar='XX', foo='YY')
>>> parser.parse_args(['XX', '--foo'])
Namespace(bar='XX', foo='c')
Although the default keyword isn't necessary in your case, since you want the value to be None if the user does not provide the flag.

python : argparse boolean arguments via command line [duplicate]

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

Mutually exclusive groups of arguments? [duplicate]

This question already has answers here:
Python argparse mutual exclusive group
(4 answers)
Closed 6 years ago.
I have a single Python file which includes unit tests in the source code. It works like this:
parser = argparse.ArgumentParser()
parser.add_argument('--test', action='store_true', help="Run unit tests and return.")
args = parser.parse_args()
if args.test:
testsuite = unittest.TestLoader().loadTestsFromTestCase(SanitizeTestCase)
unittest.TextTestRunner(verbosity=1).run(testsuite)
If args.test is false, the program runs as expected. I don't want to have to make this into an entire setuptools project, it's a pretty simple script with some unit tests to evaluate that it does what it's supposed to.
I now find myself needing to parse other arguments, and that's where things start to fall apart. --test is a mutually exclusive parameter and all of the other parameters don't apply if --test is passed.
Is there a way to have mutually-exclusive argument groups in argparse?
There is a mutually exclusive group mechanism, but all the arguments in that group are mutually exclusive. You can't say, --test xor any of the other others.
But such a group doesn't do anything profound. It adds some markings to the usage line (try it), and it complains when your user (yourself?) violates the exclusivity.
You can do the same things yourself, and fine tune them. You can give the parser a custom usage line. And after parsing you can choose to ignore conflicting values, or you can choose to raise your own error message (parser.error('dumb user, cant you read ...?')). You could, for example, ignore all the other arguments, regardless of value, if this args.test is True.

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"

Categories

Resources