JS.
JS.

Reputation: 16097

Use function/method defaults instead of argparse defaults

When using argparse, I've not found an elegant/DRY way to use the function/method defaults instead of the defaults passed by argparse.

For example, I have foreign code I am loathe to modify. How do I tell argparse (or elegantly handle after argparse) to use the function defaults if the user does not pass in a clear preference on the command-line?

import argparse

def foreign_func(fav_color="blue"):
    print(fav_color)

clparser = argparse.ArgumentParser()
clparser.add_argument(
    "--color",
    default=None,
    help="Enter color")
clargs = clparser.parse_args()

foreign_func(fav_color=clargs.color)

This will print 'None', instead of "blue"

My default approach is something like:

if clparser.color:
    foreign_func(fav_color=clargs.color)
else:
    foreign_func()

but this seems clunky, especially if there are multiple command-line options.

EDIT: Hi folks, thank you for the fast feedback. To clarify, although it would be nice for argparse to display 'blue' in it's help, that's not my goal.

I'm looking for:

my_prog --color=red
>>> red

my_prog 
>>> blue

With the code above, the program is outputting 'None', not "blue"

Upvotes: 4

Views: 270

Answers (3)

JS.
JS.

Reputation: 16097

Combining answers/tips from @Scott Hunter, @wim, and Adam Smith, the following solution seems workable.

This solution hinges on converting the argparse NameSpace object to a dict, then "splitting" that dict before passing to foreign_func. Along with the argparse.SUPPRESS option, the dict passed in will have no entry for missing command-line arguments. This avoids "stomping" on foreign_funcs defaults.

Note: I have not tested this approach in the case that foreign_func also has positional arguments, so YMMV.

import argparse

def foreign_func(fav_color="blue"):
    print(fav_color)

clparser = argparse.ArgumentParser()

clparser.add_argument(
    "--color",
    # Use 'dest' option if your command-line option name differs
    # from the function's argument name
    dest="fav_color",
    # 'argparse.SUPPRESS' will add an entry into the clargs "dict"
    # only if the option is passed on the command-line.
    default=argparse.SUPPRESS,
    help="Enter color")

clargs = clparser.parse_args()

print("DBG:", vars(clargs))
foreign_func(**vars(clargs))

Testing:

python my_prog.py --color=red
>>> red

python my_prog.py 
>>> blue

Upvotes: 1

Blckknght
Blckknght

Reputation: 104712

You can use inspect to get the function's default parameter value, and use that as the default in the argparse code.

import argparse
import inspect

def foreign_func(fav_color="blue"):
    print(fav_color)

clparser = argparse.ArgumentParser()
clparser.add_argument(
    "--color",
    default=inspect.signature(foreign_func).parameters['fav_color'].default,
    help="Enter color")
clargs = clparser.parse_args()

foreign_func(fav_color=clargs.color)

Note that this will behave a bit oddly if there is no default in the function (if it's a required parameter). In that case, the default value will be the inspect._empty sentinel value, rather than an actually meaningful value. But your code seems to be assuming that the parameter is always optional, so I'm going with that too.

Upvotes: 3

Scott Hunter
Scott Hunter

Reputation: 49803

You can use a dictionary to specify your arguments by name; if a name is missing, that parameter will use its default. Example:

def func(a=1,b=2):
    print("a=",a,"b=",b)

func()
func(**{})
func(**{'a':10})

will print

a= 1 b= 2
a= 1 b= 2
a= 10 b= 2

Upvotes: 2

Related Questions