7

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

Nagabhushan S N
  • 6,407
  • 8
  • 44
  • 87
  • when it is supposed to be a default value, you should have less arguments than when the user has to provide one, yes? You can just check how many arguments you are provided, and wrap that in a conditional. – Paritosh Singh Dec 15 '18 at 09:04
  • Thing is I have multiple such arguments. So, I want to know which ones were provided by user and for which ones, default value was picked up. – Nagabhushan S N Dec 15 '18 at 09:08
  • Presumably you know what those defaults are, so you can check whether the value is the same as the default. – jonrsharpe Dec 15 '18 at 09:10
  • There is a possibility that user might input the default value :P – Nagabhushan S N Dec 15 '18 at 09:11
  • 1
    https://stackoverflow.com/questions/4480075/argparse-optional-positional-arguments seems helpful for you, but it's not really a duplicate so I'm not marking it. – Karl Knechtel Dec 15 '18 at 09:20

2 Answers2

4

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

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
1

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.

hpaulj
  • 221,503
  • 14
  • 230
  • 353