Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to differentiate between default value and user given value?

I'm using Python 3 argparse to get command-line arguments. I have an optional argument with a default value. When I read its value, is it possible to know if the user manually inputted the value or is it default one?

Earlier, I had designed as above. Now, I want to store preferences from previous usage. So, if a user didn't input any value to the optional argument, I want to take the value from previous usage (if any). So, I want to determine whether the value came as a default value or user given value.

Edit 1: I have multiple arguments like this. I know I can remove default value for the arguments and assign them programmatically later. I just want to know if there is a simpler solution to this

like image 820
Nagabhushan S N Avatar asked Dec 15 '18 09:12

Nagabhushan S N


2 Answers

if a user didn't input any value to the optional argument, I want to take the value from previous usage (if any). So, I want to determine whether the value came as a default value or user given value.

Then you don't want to supply a default value; make the parameter optional (e.g. Argparse optional positional arguments? ), and then explicitly check for its presence or absence in the resulting Namespace object and do the appropriate thing in your code logic - presumably, check for a value in previous usage and then supply a default as a last resort. (Think of default values as a shortcut logic to handle the absence of the value; you need more complicated logic, so better to replace that system than to try to work around it.)

like image 76
Karl Knechtel Avatar answered Nov 01 '22 09:11

Karl Knechtel


The surest way of identifying whether the user has provided a value is to leave the default at the default None. There's nothing the user can say that will produce the same thing. Then after parsing all you need to do is:

if args.myval is None:
    # no user input, set my own special default
else:
    # user value

If you set the default=argparse.SUPPRESS, the attribute will be omitted from the args namespace. But testing for that case it a bit more awkward.

args.myval

will raise an error, which you could catch. Or you could use hasattr(args, 'myval'). Or handle the missing key invars(args)`.

Internally parse_args maintains a list (set actually) of Actions that it has 'seen'. That's used to test for required Actions. But that isn't available to users. If it were available, sophisticated users could perform a wider variety of co-occurrence tests.

In [28]: import argparse
In [29]: parser = argparse.ArgumentParser()
In [30]: a1 = parser.add_argument('-f','--foo')

Test without a value:

In [31]: args = parser.parse_args([])
In [32]: args
Out[32]: Namespace(foo=None)         # foo is present
In [33]: args.foo is None
Out[33]: True

Change the default to SUPPRESS:

In [35]: a1.default = argparse.SUPPRESS
In [36]: args = parser.parse_args([])
In [37]: args
Out[37]: Namespace()          # foo is missing
In [38]: args.foo
AttributeError: 'Namespace' object has no attribute 'foo'

We could put args.foo in a try: except AttributeError block.

In [39]: hasattr(args, 'foo')
Out[39]: False

Or dictionary testing for missing value

In [46]: vars(args)
Out[46]: {}
In [47]: vars(args).get('foo') is None
Out[47]: True
In [48]: 'foo' in vars(args)
Out[48]: False

A default is most useful when it provides a valid, useful value, and it saves you from extra testing after parsing.

like image 23
hpaulj Avatar answered Nov 01 '22 10:11

hpaulj