classification
Title: Change default behavior of arguments with type bool when options are specified
Type: behavior Stage: resolved
Components: Library (Lib) Versions: Python 3.4
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: josh.r, krichter, paul.j3, r.david.murray
Priority: normal Keywords:

Created on 2014-04-12 21:58 by krichter, last changed 2014-05-28 21:45 by paul.j3. This issue is now closed.

Messages (8)
msg215983 - (view) Author: Karl Richter (krichter) Date: 2014-04-12 21:58
As arguments with type bool are the only ones whose values can be manipulated without passing an option to the argument on CLI, the default behavior for those should be changed from ignoring options to failing when options are specified. Consider the following example:
<code>

import argparse

cache_size_default=1000
cache_size_option = "c"
cache_size_option_long = "cache-size"
start_db_default = False
start_db_option = "t"
start_db_option_long = "start-db"


parser = argparse.ArgumentParser(description='Process some integers.')               
parser.add_argument("-%s" % start_db_option, "--%s" % start_db_option_long, default=start_db_default, type=bool, nargs='?',
                   help='@TODO', dest=start_db_option_long)
parser.add_argument("-%s" % cache_size_option, "--%s" % cache_size_option_long, type=int, nargs='?', default=cache_size_default, 
                   help='size of osm2pgsql cache', dest=cache_size_option_long)

def osm_postgis_transform(cache_size=cache_size_default, start_db=start_db_default):
    print(start_db, cache_size)

if __name__ == "__main__":
    args = vars(parser.parse_args())
    osm_postgis_transform(cache_size=args[cache_size_option_long], start_db=args[start_db_option_long])
</code>

When the script is invoked with 
<code>
python issue-argparse.py --start-db False --cache-size 2000
</code>
The value for start-db is True which is not really intuitive. Changing the default behavior to either fail at argument parsing or overwrite the argument value would be an improvement in my opinion.
msg215986 - (view) Author: Karl Richter (krichter) Date: 2014-04-12 22:48
I've been mistaken about the behavior if no argument is specified (then the argument value is None), so this is a bug!
msg215992 - (view) Author: Josh Rosenberg (josh.r) * (Python triager) Date: 2014-04-13 01:30
If your goal is to get a boolean on/off switch, that's what action='store_true' is for. You don't need to specify nargs or type at all; using bool as the type means it wants an argument, and will pass the string form of the argument to the bool constructor; since only the empty string is False, it would be quite difficult to pass it intuitively.
msg215996 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2014-04-13 04:22
Yeah, this is a bit non-obvious, but it is a specific instance of the general way that argparse handles types.  So as far as I can see there really isn't anything to do here.
msg216040 - (view) Author: Karl Richter (krichter) Date: 2014-04-14 00:06
That's a pity, I still think it's confusing. Thanks for your feedback!
msg219249 - (view) Author: paul j3 (paul.j3) * (Python triager) Date: 2014-05-28 03:55
Last year someone asked on Stackoverflow about using 'type=bool'.  My answer is at:

http://stackoverflow.com/questions/15008758/parsing-boolean-values-with-argparse/19233287#19233287

'type' is supposed to be a function that takes a string, and converts to a desired Python object (e.g. number), and in the process validates it.

The builtin 'bool()' is not a good choice for this. 'bool("")' returns False, all other strings return True, that includes strings like "false", "no", "False".  If you want those strings to be interpreted as boolean False, you need to write your own 'str2bool' function.

But normally boolean values are entered via 'store_true' and 'store_false' actions.
msg219262 - (view) Author: Karl Richter (krichter) Date: 2014-05-28 10:09
@paul.j3 That's interesting [1]. Documenting argparse.register seems crucial to me (-> reopen or file a new request?). 

After dealing more with the very sophisticated and complex functionality of argparse I'm sure that this is the only use case where such an unintuitive result may happen, so it's worth adding an explicit negative example in the docs of argparse.add_argument with a hint to the docs about "way that argparse handles types".

----------
[1] (and a very good example for missing capabilities of Q&A sites with regard to the relation of votes of your much better answer and the one with the highest number of votes)
msg219305 - (view) Author: paul j3 (paul.j3) * (Python triager) Date: 2014-05-28 21:45
In http://bugs.python.org/issue11588#msg212243  I explore the option of using decorators to 'register' custom functions with the parser.  There I was adding exclusive/inclusive tests, but the same approach could be used to register custom types and actions.

I should start a new issue re. documenting the use of 'register'.
History
Date User Action Args
2016-05-10 18:37:48r.david.murraylinkissue26994 superseder
2014-05-28 21:45:45paul.j3setmessages: + msg219305
2014-05-28 10:09:27krichtersetmessages: + msg219262
2014-05-28 03:55:33paul.j3setnosy: + paul.j3
messages: + msg219249
2014-04-14 00:06:01krichtersetmessages: + msg216040
2014-04-13 04:22:31r.david.murraysetstatus: open -> closed

nosy: + r.david.murray
messages: + msg215996

resolution: not a bug
stage: resolved
2014-04-13 01:30:43josh.rsetnosy: + josh.r
messages: + msg215992
2014-04-12 22:48:18krichtersetmessages: + msg215986
2014-04-12 21:58:04krichtercreate