This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: argparse error with option with optional value
Type: behavior Stage:
Components: Library (Lib) Versions: Python 3.8
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: paolobenve, paul.j3
Priority: normal Keywords:

Created on 2021-10-29 17:06 by paolobenve, last changed 2022-04-11 14:59 by admin.

Messages (5)
msg405322 - (view) Author: Paolo Benvenuto (paolobenve) Date: 2021-10-29 17:06
I'm using argparse with this code:

parser = argparse.ArgumentParser(
    description='Scan a media tree in order to generate cache files suitable for showing a beautiful web gallery',
  )
  parser.add_argument(
    "config_file_or_album_path",
    help="the config file, if it's the only positional argument, or the album path, if two positional arguments are supplied"
  )
  parser.add_argument(
    "cache_path",
    nargs="?",
    default="",
    help="the cache path, if two positional arguments are supplied; otherwise, nothing"
  )
  parser.add_argument(
    "-s",
    "--periodic-save",
    nargs="?",
    type=int,
    const=5,
    default=0,
    dest="periodic_save",
    metavar="MINUTES",
    help="runs the scanner in a more robust way, saving json files every X minutes, where X is the value given, or 5 if no value is given; 0 means non autosaving"
  )
  parser.add_argument(
    "-j",
    "--recreate-json-files",
    action='store_true',
    dest="recreate_json_files",
    help="completely recreate the json files cache"
  )
  parser.add_argument(
    "-r",
    "--recreate-reduced-photos",
    action='store_true',
    dest="recreate_reduced_photos",
    help="completely recreate reduced photo cache files"
  )
  parser.add_argument(
    "-t",
    "--recreate-thumbnails",
    action='store_true',
    dest="recreate_thumbnails",
    help="completely recreate thumbnail cache files"
  )
  parser.add_argument(
    "-v",
    "--recreate-transcoded-videos",
    action='store_true',
    dest="recreate_transcoded_videos",
    help="completely recreate transcoded video cache files"
  )
  parser.add_argument(
    "-a",
    "--recreate-all",
    action='store_true',
    dest="recreate_all",
    help="completely recreate the cache: json files, reduced photos, thumbnails and transcoded videos; same as -jrtv"
  )
  parser.add_argument("--version", action="version", version='%(prog)s v5.3.10')
  args = parser.parse_args()

Running the app without parameter gives me:

$ myapp
usage: scanner [-h] [-s [MINUTES]] [-j] [-r] [-t] [-v] [-a] [--version] config_file_or_album_path [cache_path]
myapp: error: the following arguments are required: config_file_or_album_path

(the last line doesn't matter here, a required parameter is missing).

The -s option is optional and has an optional value, but:

$ myapp -s myfile
usage: scanner [-h] [-s [MINUTES]] [-j] [-r] [-t] [-v] [-a] [--version] config_file_or_album_path [cache_path]
scanner: error: argument -s/--periodic-save: invalid int value: '/my/file'

I'm expecting that, since there is a mandatory positional argument, myfile is interpreted as such, and only after that, -s is recognized as the optional argument without value.
msg405523 - (view) Author: paul j3 (paul.j3) * (Python triager) Date: 2021-11-02 17:17
This is too big of an example for this board; I think it should have been asked on StackOverFlow.  Or maybe trimmed do to a more compact example.

But in any case, this is normal behavior for argparse.  Type checking, here 'int', is done after the string is allocated to the '-s' argument.

Reserving that string for the required positional requires a form of look ahead that argparse does not currently do.  I think there's an old bug/issue on the topic, but the fix was, if I remember correctly, too complex to simply drop in.

I may look that up later.
msg405561 - (view) Author: paul j3 (paul.j3) * (Python triager) Date: 2021-11-02 22:09
https://bugs.python.org/issue9338
argparse optionals with nargs='?', '*' or '+' can't be followed by positionals

As you can see this is an old issue, but still too big for a quick fix.

As a general rule, don't use `nargs` like this where there's ambiguity as to how many values will be allocated to the argument.
msg405562 - (view) Author: Paolo Benvenuto (paolobenve) Date: 2021-11-02 22:11
> As a general rule, don't use `nargs` like this where there's ambiguity as to how many values will be allocated to the argument.

What could I use instead of nargs?
msg405564 - (view) Author: paul j3 (paul.j3) * (Python triager) Date: 2021-11-02 22:25
Put the required positional first

$ myapp myfile -s

or one of the store_true arguments

$ myapp -s -j myfile

I think

$ myapp -s -- myfile

will work as well, but that needs to be tested.  

The '-s' has to be followed by something won't be confused for an argument, such as nothing, or a flag.
History
Date User Action Args
2022-04-11 14:59:51adminsetgithub: 89836
2021-11-02 22:25:15paul.j3setmessages: + msg405564
2021-11-02 22:11:18paolobenvesetmessages: + msg405562
2021-11-02 22:09:14paul.j3setmessages: + msg405561
2021-11-02 17:17:19paul.j3setnosy: + paul.j3
messages: + msg405523
2021-10-29 17:06:05paolobenvecreate