1

Can I extract only the parsed arguments from the command line, ignoring all default arguments? If the user passed the default value for an argument, I would like it to appear in the subset as well:

parser = argparse.ArgumentParser()
parser.add_argument("--a",type=str,default='a')
parser.add_argument("--b",type=str,default='b')
parser.add_argument("--c",type=str,default='c')
parser.add_argument("--d",type=str,default='d')
python run.py --a "e" --b "b"

I would like to have

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--a",type=str,default='a')
    parser.add_argument("--b",type=str,default='b')
    parser.add_argument("--c",type=str,default='c')
    parser.add_argument("--d",type=str,default='d')

    from_cli = parse_from_cli(parser) # from_cli = {'a':'e','b':'b'}
6
  • 3
    Why do you have defaults if you don't want them to be used when the user omits the option? Commented Jun 13, 2021 at 5:24
  • use-case has been added. Commented Jun 13, 2021 at 12:10
  • Read the old dictionary in first, and use the values there as the defaults in argparse. Commented Jun 13, 2021 at 16:58
  • If default=argparse.SUPPRESS, the args will only contain attributes set by the user. Include a print(args) while debugging to see clearly what the parser has produced. Commented Jun 13, 2021 at 17:42
  • But that way I won't be able to set default parameters. These are two orthogonal things. Parse the arguments, and understand which flags were passed by the user. I would like the latter. Commented Jul 14, 2021 at 13:13

3 Answers 3

3
+50

You can provide parse_args with a namespace argument. In this case, the values in the provided namespace will take precedence over the defaults set in the parser. We just need to create a "mask" namespace and a sentinel object to cover the defaults. It does not appear there is a public method for extracting the args from a parser, but we can call the parser twice and use the first call to create the mask.

import argparse

NOTHING = object()

parser = argparse.ArgumentParser()
parser.add_argument('--foo', default=1, type=int)
parser.add_argument('--bar', default=2, type=int)

args = vars(parser.parse_args())
mask = argparse.Namespace(**{arg: NOTHING for arg in args})

masked_namespace = parser.parse_args(namespace=mask)
masked_args = {
    arg: value
    for arg, value in vars(masked_namespace).items()
    if value is not NOTHING
}

print(masked_args)

In use you'll get:

$ python foo.py
{}
$ python foo.py --bar 11
{'bar': 11}
Sign up to request clarification or add additional context in comments.

4 Comments

This is a great answer! I wasn't familiar with that and this indeed helps. Unfortunately, this was not the question (I deleted the use case section as it was probably a bit misleading). I am trying to extract a namespace/dict containing only the flags that came from the cli. Isn't it possible?
I've updated the answer to be independent of the removed "use case" section.
A-m-a-z-i-n-g. Thank you
Wow! This is so useful for my programs when I want to resume from a breakpoint but with different properties than originally specified. Thanks!
0

You can omit the default= argument to the add_argument function, then arguments that the user doesn't specify explicitly will resolve to None.

Comments

0

You can keep the old arguments in a separate config file, then read and write back changed arguments.

This should look something like this:

import argparse

args = {'a':None,'b':None}

with open("config","r") as config:

    parser = argparse.ArgumentParser()
    parser.add_argument("--a",type=str)
    parser.add_argument("--b",type=str)

    newargs = parser.parse_args()


    # set new arguments
    args['a'] = newargs.a
    args['b'] = newargs.b

    # read previous arguments
    for line in config.readlines():
        if ":" in line : 
            temp = line.strip().split(":")
            if not args[temp[0]] :
                args[temp[0]] = temp[1]

with open("config","w") as config:

    # write back new arguments with the newargs
    lines = []
    for key in args.keys() :
        lines.append("{}:{}\n".format(key,args[key]))

    config.writelines(lines)

print(args)

The encoding in the config file could of course be different, I choose this one for debugging purpose.

1 Comment

I deleted the Use case section as it seems to mislead answers, sorry! I simply want to parse only the arguments provided from the command line

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.