Shubham Chaudhary
Shubham Chaudhary

Reputation: 51023

argparse : ValueError when using --help or -h option with nargs

When using argparse I get this weird ValueError for help option. All other options work fine. Only the help option is not working, which is weird.

Here is the error message:

chaudhary@localhost:~/code/python/logmein$ ./logmein.py --bla
Usage:  ./logmein.py  [filename <Default: .login.txt>] || [password] || [username password]
chaudhary@localhost:~/code/python/logmein$ ./logmein.py --boo
Usage:  ./logmein.py  [filename <Default: .login.txt>] || [password] || [username password]
chaudhary@localhost:~/code/python/logmein$ ./logmein.py -b
Usage:  ./logmein.py  [filename <Default: .login.txt>] || [password] || [username password]
chaudhary@localhost:~/code/python/logmein$ ./logmein.py -h
Unexpected Error: <class 'ValueError'>
Details: unsupported format character 'p' (0x70) at index 1
Traceback (most recent call last):
  File "./logmein.py", line 222, in <module>
    main(sys.argv)
  File "./logmein.py", line 158, in main
    args, otherthings = parser.parse_known_args()
  File "/usr/lib64/python3.3/argparse.py", line 1737, in parse_known_args
    namespace, args = self._parse_known_args(args, namespace)
  File "/usr/lib64/python3.3/argparse.py", line 1943, in _parse_known_args
    start_index = consume_optional(start_index)
  File "/usr/lib64/python3.3/argparse.py", line 1883, in consume_optional
    take_action(action, args, option_string)


 File "/usr/lib64/python3.3/argparse.py", line 1811, in take_action
    action(self, namespace, argument_values, option_string)
  File "/usr/lib64/python3.3/argparse.py", line 1015, in __call__
    parser.print_help()
  File "/usr/lib64/python3.3/argparse.py", line 2339, in print_help
    self._print_message(self.format_help(), file)
  File "/usr/lib64/python3.3/argparse.py", line 2323, in format_help
    return formatter.format_help()
  File "/usr/lib64/python3.3/argparse.py", line 276, in format_help
    help = self._root_section.format_help()
  File "/usr/lib64/python3.3/argparse.py", line 206, in format_help
    func(*args)
  File "/usr/lib64/python3.3/argparse.py", line 293, in _format_usage
    usage = usage % dict(prog=self._prog)
ValueError: unsupported format character 'p' (0x70) at index 1

Here is my code

# Parse command line arguments
from argparse import ArgumentParser
usage = "%prog [-f credential_file]"
parser = ArgumentParser(usage=usage)
parser.add_argument("-f", "--file", type=str, dest="file",
                    help="Use the specified file")
parser.add_argument("-o", "--logout", action='store_true', dest="logout",
                    help="Logout")
#parser.add_argument('otherthings', nargs='*')
#args = parser.parse_args()
args, otherthings = parser.parse_known_args()
argc = len(otherthings)

if args.file != None:
    username, password = parse_file_for_credential(args.file) 

Now I can't find out why this ValueError comes and that too only for --help option.

Upvotes: 1

Views: 1938

Answers (2)

Sean Vieira
Sean Vieira

Reputation: 159915

The issue is that when argparse attempts to interpolate your usage string Python sees the following character sequence: %p. Since p is not a valid conversion flag, Python raises the ValueError.

You can generate this error yourself at the REPL:

>>> "This is valid: %s" % "magic"
"This is valid: magic"

>>> "This will raise a ValueError: %p" % "magic"
ValueError: unsupported format character 'p' (0x70) 

The reason this is happening is because you use %prog rather than the correct %(prog)s (note the parenthesis around the key in %s). Change usage = "%prog [-f credential_file]" to usage = "%(prog)s [-f credential_file]" as Rob suggests and everything will work.

Why does argparse use this formatting syntax?

argparse provides metadata like the program's name via named format arguments to allow the arguments to appear in any order, and to allow for repetition. Positional arguments cannot be re-used and must appear in the same order as they are provided:

>>> "If I want another %s then I need to add another %s" % ("param", "placeholder")
"If I want another param then I need to add another placeholder"
# There is no way to change the order around to be
# "If I want another placeholder then I need to add another param"
# without changing the order of the parameters passed to `%`

while named arguments can be repeated and used in any order:

>>> s = "Any order, %(two)s / %(one)s. Repeats %(one)s / %(two)s are possible"
>>> s % {"one": "first", "two": "last"}
"Any order, last / first. Repeats first / last are possible"

Upvotes: 3

Robᵩ
Robᵩ

Reputation: 168626

Quoting the documentation:

Note that the program name, whether determined from sys.argv[0] or from the prog= argument, is available to help messages using the %(prog)s format specifier.

Try usage = "%(prog)s [-f credential_file]"

Upvotes: 3

Related Questions