classification
Title: ArgumentParser should support bool type according to truth values
Type: enhancement Stage: resolved
Components: Library (Lib) Versions: Python 3.9
process
Status: closed Resolution: rejected
Dependencies: Superseder:
Assigned To: rhettinger Nosy List: Zach Beniash, dkg, matrixise, paul.j3, rhettinger
Priority: normal Keywords: patch

Created on 2019-07-11 15:10 by Zach Beniash, last changed 2019-11-13 16:42 by rhettinger. This issue is now closed.

Pull Requests
URL Status Linked Edit
PR 14709 closed python-dev, 2019-07-11 15:15
Messages (7)
msg347686 - (view) Author: Zach Beniash (Zach Beniash) * Date: 2019-07-11 15:10
Today when using argparse.ArgumentParser it seems that the bool type is not supported in a logical way.
Foe example:
-------------------------------------
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--mybool", type=bool)
parsed_args = parser.parse(["--mybool", "False"])
--------------------------------------
parsed_args.my_bool evaluates to True

Instead we should expect to evaluate False here.
msg347704 - (view) Author: paul j3 (paul.j3) * (Python triager) Date: 2019-07-11 20:06
This is an old topic, though it may be easier to find a relevant StackOverflow thread.

https://stackoverflow.com/a/19233287/901925  (a 2013 thread)

I and others have explained that the `type` parameter is supposed to be a callable.  The default Python `bool` function only returns False for an empty string.  Previous discussions have suggested writing a function that parses strings like 'False' and 'No'.  It's nice that you have found a 'distutils.utils.strtobool' that does this well.

But I have several qualms about the proposed patch:

- do we need to define a 'bool' type?  We already has 'store_true' and 'store_false' actions.

- does this function need a 'bool' wrapper?

- do we need another import?  There's a relatively recent bug/issue that tries to speed up argparse by reducing the number of imports.

- if added, should this type be registered as the object bool or string 'bool'?  

Seems to me that the patch could be simplified to:

     self.register('type', 'bool', _strtobool)

According the original developer `type=bool` should be allowed (even if it doesn't do what many users expect):

https://bugs.python.org/issue14392

I point out that users are used to providing strings for 'action', but not for 'type'.  The registry mechanism can be used to provide specialized type but so far only 'None' is registered.

https://bugs.python.org/issue26994

https://bugs.python.org/issue24754

This proposed patch might be innocuous, but it does break a consistent behavior - that 'type' should be a callable, and 'bool' is a standard Python function.

An alternative is to add to the docs a note (in the 'type' section) that

    from distutils.util import strtobool
    type=strtobool

could be used to parse strings as booleans.
msg347989 - (view) Author: paul j3 (paul.j3) * (Python triager) Date: 2019-07-15 19:21
The use of `bool` as registry key only works because that object already exists as a builtin.  This amounts a form of shadowing, and sets a dangerous precedent.

    type=bool

should work as documented at

https://docs.python.org/3/library/functions.html?highlight=bool#bool

even if that is not what a casual user would expect.
msg349476 - (view) Author: paul j3 (paul.j3) * (Python triager) Date: 2019-08-12 15:40
This patch lacks proper documentation, and is too English language specific.

For users who assume 'type' means a class rather than function (callable) this change does not need documentation, since it accommodates that assumption.  But more experienced users, or ones who read the documentation with care, this change could be puzzling.  "Why does 'bool' work?"  Admittedly they are unlikely to try it.  Still, we are deviating the documented behavior.

We also need to pay attention to internationalization.  Argparse wraps most error messages and string displays in 'gettext'.  There isn't any other part of argparse that assumes that sys.argv strings have English meanings.
msg352308 - (view) Author: Stéphane Wirtel (matrixise) * (Python committer) Date: 2019-09-13 12:19
What will happen if we want to translate "true", "yes", ...?

-1
msg356332 - (view) Author: Daniel Kahn Gillmor (dkg) Date: 2019-11-10 17:19
this is a common enough question, and enough people want this behavior, that argparse should supply it.

I'm imagining that:  

   type='bool'

would be fine for triggering this behavior, instead of the shadowing that could happen with:

   type=bool

so if type='bool' is supplied, the following things would happen:

 * argparse could use strtobool to interpret the incoming string
 * the default metavar would look something like "{true,false}"
 * followon packages like argcomplete could enumerate the range of different values that strtobool accepts

This is still english-specific -- but it's because strtobool is english-specific, and fixes in strtobool would automatically fix type='bool' here.
msg356336 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2019-11-10 21:08
Right now "type" has a clear and understandable action of invoking a callable.   Imbuing "type" with other semi-magical capabilities opens a new can of worms.

Unless some persuasive new commentary appears in the next few days, I'm going to close this feature request and the associated PR.

At some point, we should consider adding a recipes/faq section to the docs.  That would help people bridge from their goals to the actual capabilities offered by the module.

Also, we should make strong suggestions about the separation of responsibilities between the parser and the downstream code that acts on the parsed variables (much like the discussion in the templating world about keeping business logic outside of the template and instead focusing on formatting logic).

For the case at hand, there are several ways to go:

      # Proposed new magic with hard-wired
      # truthy/false words: yes True 1 true Y si oui ...
      parser.add_argument("--mybool", type='bool')

      # Existing capability where the user controls
      # exactly which logic to apply
      parser.add_argument("--mybool", type=str_to_bool)

      # Existing capability with downstream processing
      parser.add_argument("--mybool", choices={'True', 'False', 'Yes', 'No'}
      args = parser.parse_args()
      mybool = args.mybool in {'True', 'Yes'}

      # Existing capability with explicit flag arguments
      # (the highly upvoted "canonical" solution StackOverflow)
      parser.add_argument('--feature', dest='feature', action='store_true')
      parser.add_argument('--no-feature', dest='feature', action='store_false')
      parser.set_defaults(feature=True)
History
Date User Action Args
2019-12-31 01:26:28josh.rlinkissue39167 superseder
2019-11-13 16:42:20rhettingersetstatus: open -> closed
resolution: rejected
stage: patch review -> resolved
2019-11-10 21:08:49rhettingersetmessages: + msg356336
2019-11-10 21:08:36rhettingersetmessages: - msg356334
2019-11-10 21:02:00rhettingersetversions: - Python 2.7, Python 3.5, Python 3.6, Python 3.7, Python 3.8
nosy: + rhettinger

messages: + msg356334

assignee: rhettinger
type: behavior -> enhancement
2019-11-10 17:19:46dkgsetnosy: + dkg
messages: + msg356332
2019-09-13 12:19:21matrixisesetnosy: + matrixise
messages: + msg352308
2019-08-12 15:40:58paul.j3setmessages: + msg349476
2019-07-15 19:21:32paul.j3setmessages: + msg347989
2019-07-11 20:06:49paul.j3setmessages: + msg347704
2019-07-11 15:24:42xtreaksetnosy: + paul.j3
2019-07-11 15:15:09python-devsetkeywords: + patch
stage: patch review
pull_requests: + pull_request14509
2019-07-11 15:10:32Zach Beniashcreate