Argparse not parsing boolean arguments?
Asked Answered
S

2

10

I am trying to make a build script like this:

import glob
import os
import subprocess
import re
import argparse
import shutil

def create_parser():
    parser = argparse.ArgumentParser(description='Build project')

    parser.add_argument('--clean_logs', type=bool, default=True,
                        help='If true, old debug logs will be deleted.')

    parser.add_argument('--run', type=bool, default=True,
                        help="If true, executable will run after compilation.")

    parser.add_argument('--clean_build', type=bool, default=False,
                        help="If true, all generated files will be deleted and the"
                        " directory will be reset to a pristine condition.")

    return parser.parse_args()


def main():
    parser = create_parser()
    print(parser)

However no matter how I try to pass the argument I only get the default values. I always get Namespace(clean_build=False, clean_logs=True, run=True).

I have tried:

python3 build.py --run False
python3 build.py --run=FALSE
python3 build.py --run FALSE
python3 build.py --run=False
python3 build.py --run false
python3 build.py --run 'False'

It's always the same thing. What am I missing?

Spiculate answered 2/4, 2020 at 19:42 Comment(5)
We use action="store_true" to deal with this kind of problem: docs.python.org/3.8/library/argparse.html#actionCandleberry
Does this answer your question? Parsing boolean values with argparseFarinose
@IdeaO. No that answer does not explain why arparse isn;t parsing the input. My question is different.Spiculate
Try bool("False") or bool("FALSE"). The only string that returns False is bool(""). You have to write your own function that recognizes strings like 'False' or 'No' as False. The builtin bool does not do that for you.Scoria
An old answer: #15009258Scoria
F
18

You are misunderstanding how the argparse understands the boolean arguments.

Basically you should use action='store_true' or action='store_false' instead of the default value, with the understanding that not specifying the argument will give you the opposite of the action, e.g.

parser.add_argument('-x', type=bool, action='store_true')

will cause:

python3 command -x

to have x set to True and

python3 command

to have x set to False.

While action=store_false will do the opposite.


Setting bool as type does not behave as you expect and this is a known issue.

The reason for the current behavior is that type is expected to be a callable which is used as argument = type(argument). bool('False') evaluates to True, so you need to set a different type for the behavior you expect to happen.

Farinose answered 2/4, 2020 at 19:50 Comment(2)
A more recent bug/issue bugs.python.org/issue37564 found that distutils.util.strtobool can parse a variety of yes/no (English only) words to True/False values.Scoria
I had to remove the "type" argument to make it work. Like this: parser.add_argument("-Var1", action="store_true") since parser.add_argument("-Var1", type=bool, action="store_true") caused an error.Recompense
D
6

Argument run is initialized to True even that you pass --run False

Below code based on this great answer is workaround around this issue:

import argparse

def str2bool(v):
    if isinstance(v, bool):
        return v
    if v.lower() in ('yes', 'true', 't', 'y', '1'):
        return True
    elif v.lower() in ('no', 'false', 'f', 'n', '0'):
        return False
    else:
        raise argparse.ArgumentTypeError('Boolean value expected.')

def main():
    ap = argparse.ArgumentParser()
    # List of args
    ap.add_argument('--foo', type=str2bool, help='Some helpful text')
    # Importable object
    args = ap.parse_args()
    print(args.foo)


if __name__ == '__main__':
    main()
Disparagement answered 13/9, 2021 at 14:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.