Closed pdc1 closed 2 years ago
Doesn't that already work? As far as I can see argparse doesn't infer the "choices" from an argument passed into add_subparsers
but rather from the first arguments passed to subparsers.add_parser
. To give a full example:
import argparse
import sys
from typing import List, Literal, Union
from typed_argparse import TypedArgs, WithUnionType
class CommonArgs(TypedArgs):
verbose: bool
class ArgsFoo(CommonArgs):
mode: Literal["foo"]
file: str
class ArgsBar(CommonArgs):
mode: Literal["bar"]
src: str
dst: str
Args = Union[ArgsFoo, ArgsBar]
def parse_args(args: List[str] = sys.argv[1:]) -> Args:
parser = argparse.ArgumentParser()
parser.add_argument("--verbose", action="store_true", help="Verbose")
subparsers = parser.add_subparsers(
help="Available sub commands",
dest="mode",
required=True,
)
parser_foo = subparsers.add_parser("foo")
parser_foo.add_argument("file", type=str)
parser_bar = subparsers.add_parser("bar")
parser_bar.add_argument("--src", required=True)
parser_bar.add_argument("--dst", required=True)
return WithUnionType[Args].validate(parser.parse_args(args))
if __name__ == "__main__":
parse_args()
Behaves as follows:
There is just the minor redundancy that subparsers.add_parser
has to duplicate the value from the Literal. It might be possible to come up with a helper function that allows to reference the type + the field to get to the literal value. Something like subparsers.add_parser(get_literal_value_of(Foo, "mode"))
. Is that what you are looking for?
I was looking for protection from typos on the subparsers.add_parser()
argument, but I must have confused myself because playing around with the example I do get a runtime validation error if I change "bar" to subparsers.add_parser("baz")
for example.
Part of what I was looking for was a typing error, i.e. so mypy/etc would flag the above, but I don't see any reasonable way for it to know the class property and the argument to add_parser()
are related.
To that end, something like you suggested would help, though to be honest there are a number of other minor redundancies (like property names matching add_argument()
calls) that I'm not sure it's really necessary.
The main thing I was looking for was a failsafe against typos in validate()
, and it is already there! Sorry for the false alarm :)
To that end, something like you suggested would help, though to be honest there are a number of other minor redundancies (like property names matching add_argument() calls) that I'm not sure it's really necessary.
That's also my impression. I'll leave the issue open as a reminder just in case I come across an elegant idea...
The main thing I was looking for was a failsafe against typos in validate(), and it is already there! Sorry for the false alarm :)
No worries!
I'd close this assuming the issue has been resolved? Feel free to re-open / ask more if needed.
There is a nice example for using
Literal[]
typing for arguments (https://github.com/bluenote10/typed_argparse#convenience-functionality-to-map-literalenum-to-choices) but there does not appear to be a similar approach for subparser command definitions.Would it be possible to add something similar in this module?
Conceptually I'm thinking of something similar to the choices approach, only for the subparser, e.g. something like:
If the project had the approach shown in https://github.com/bluenote10/typed_argparse#support-for-union-useful-for-subcommand-parsing, then perhaps you'd need a union of the different
mode
definitions.Alternately it would be nice if
validate()
could catch cases where theLiteral[...]
definition did not match the argument passed tosubparsers.add_parser()
.This module has been very helpful, thanks for making it available!