This looks reasonable, except that I don't see why you do
`default="yes" if default else "no"` instead of just
`default=default`. I think if you drop that, then you'll get
args.verbose == False when the option isn't specified.
Steve
--
Where did you get that preposterous hypothesis?
Did Steve tell you that?
--- The Hiphopopotamus
Ahh, I see. I think you want to stick with your original "flag" type
approach, and then specify nargs='?', const='no' and default='no':
def flag(value):
value = value.lower()
if value in ("1", "true", "yes", "on"):
return True
elif value in ("0", "false", "no", "off"):
return False
raise ValueError("unknown flag value")
parser = argparse.ArgumentParser()
parser.add_argument("-v", "--verbose", dest="verbose",
help="Be verbose? (default: %(default)s)",
nargs='?', type=flag,
const="no", default="no")
The nargs='?' says that the argument to '-v' is optional, the
default='no' says what to use when no '-v' is present, and the
const='no' says what to use when '-v' is present but there's no
argument. With this approach, I see:
$ python temp.py -h
usage: temp.py [-h] [-v [VERBOSE]]
optional arguments:
-h, --help show this help message and exit
-v [VERBOSE], --verbose [VERBOSE]
Be verbose? (default: no)
$ python temp.py
Namespace(verbose=False)
$ python temp.py -v
Namespace(verbose=False)
$ python temp.py -v yes
Namespace(verbose=True)
I'm not sure if you also wanted something instead of "VERBOSE". If you
do, you can add something like metavar="yes|no".
Hmm... On second thought I think maybe you actually want const='yes'
here. But you can play around with it a bit and see.
The problem stems from:
repr(action.type)
Your type is a bound method, so Python tries to generate its repr as
something like "<bound method FlagAction.str2bool of ...>" and then
tries to generate the repr of the FlagAction object for the "..."
part, which then tries to generate the repr of "action.type", etc.
The solution here is just to make str2bool a function instead of a
method of FlagAction. Note that you probably don't need to raise such
a complicated TypeError if you also specify choices=self.true_choices
+ self.false_choices. Then argparse should generate decent errors on
its own I think.
Cool. Yeah, _get_kwargs() is undocumented but that should solve your
problem too.
>> Note that you probably don't need to raise such
>> a complicated TypeError if you also specify choices=self.true_choices
>> + self.false_choices. Then argparse should generate decent errors on
>> its own I think.
>
> That didn't work. I got a generic error:
> error: argument -v/--verbose: invalid str2bool value: 'x'
You can s/str2bool/flag by changing the name of your function. I
assume the remaining problem with this error message is that it
doesn't say something like "use one of ..."? That seems like a good
feature to add - feel free to file a feature request at
bugs.python.org. (Of course new features can only go into Python 3, so
for Python 2, you'll be stuck with your current workarounds.)
Good point! I guess it's been way too long since I used choices= for
anything. ;-)
Well, one simplification you could still make is to raise an
ArgumentTypeError instead of just a TypeError. That will then fill in
your "argument {}: " automatically. So you'd use it something like:
raise argparse.ArgumentTypeError(
"invalid flag value: {!r} (use any of {})".format(
value,
", ".join(self.true_choices + self.false_choices)))
Anyway, if you can think of any docs and/or code changes that would
have made this easier for you, please do file a bug report (for docs)
or feature request (for APIs) at bugs.python.org.