When I use Python's argparse or optparse command line argument parser, any unique prefix of an argument is considered valid, e.g.
$ ./buildall.py --help
usage: buildall.py [-h] [-f]
Build all repositories
optional arguments:
-h, --help show this help message and exit
-f, --force Build dirty repositories
works with --help
, --hel
, --he
for the help option as well as --forc
and --fo
for the force option.
Can this behavior be turned off somehow? I want to get an error message for incomplete arguments.
The ability to disable abbreviated long options was only added in Python 3.5. From the argparse
documentation:
The
parse_args()
method by default allows long options to be abbreviated to a prefix, if the abbreviation is unambiguous (the prefix matches a unique option) ... This feature can be disabled by setting allow_abbrev toFalse
.
So if you're on Python 3.5, you can create your parser with allow_abbrev=False
:
parser = argparse.ArgumentParser(..., allow_abbrev=False)
If you're on optparse or pre-3.5 argparse, you just have to live with abbreviated options.
Prior to Python 3.5, you would have to monkeypatch an undocumented ArgumentParser
method. Don't actually use this; it is untested and may not work with all versions (or any version) of Python. For entertainment purposes only.
import argparse
# This is a copy from argparse.py, with a single change
def _get_option_tuples(self, option_string):
result = []
# option strings starting with two prefix characters are only
# split at the '='
chars = self.prefix_chars
if option_string[0] in chars and option_string[1] in chars:
if '=' in option_string:
option_prefix, explicit_arg = option_string.split('=', 1)
else:
option_prefix = option_string
explicit_arg = None
for option_string in self._option_string_actions:
# === This is the change ===
# if option_string.startswith(option_prefix):
if option_string == option_prefix:
action = self._option_string_actions[option_string]
tup = action, option_string, explicit_arg
result.append(tup)
# single character options can be concatenated with their arguments
# but multiple character options always have to have their argument
# separate
elif option_string[0] in chars and option_string[1] not in chars:
option_prefix = option_string
explicit_arg = None
short_option_prefix = option_string[:2]
short_explicit_arg = option_string[2:]
for option_string in self._option_string_actions:
if option_string == short_option_prefix:
action = self._option_string_actions[option_string]
tup = action, option_string, short_explicit_arg
result.append(tup)
elif option_string.startswith(option_prefix):
action = self._option_string_actions[option_string]
tup = action, option_string, explicit_arg
result.append(tup)
# shouldn't ever get here
else:
self.error(_('unexpected option string: %s') % option_string)
# return the collected option tuples
return result
argparse.ArgumentParser._get_option_tuples = _get_option_tuples
p = argparse.ArgumentParser()
p.add_argument("--foo")
print p.parse_args("--f 5".split())
For those of us still stuck on python2.7 for whatever reason, this is a minimal change to locally disable prefix matching:
class SaneArgumentParser(_argparse.ArgumentParser):
"""Disables prefix matching in ArgumentParser."""
def _get_option_tuples(self, option_string):
"""Prevent argument parsing from looking for prefix matches."""
return []
Now instead of using argparse.ArgumentParser, just use SaneArgumentParser. Unlike chepner's answer, this does not require any modification to the argparse module. It is also a much smaller change. Hopefully other people stuck in python's past will find this useful.
来源:https://stackoverflow.com/questions/33900846/disable-unique-prefix-matches-for-argparse-and-optparse