lebrice / SimpleParsing

Simple, Elegant, Typed Argument Parsing with argparse
MIT License
401 stars 50 forks source link

List fields cannot be used with subparser fields #105

Open lebrice opened 2 years ago

lebrice commented 2 years ago

Describe the bug

Can't use fields with nargs="*" with a subparser field that comes after it. The values passed for the list field are incorrectly assumed to be values for the subparser, and so a "incorrect value for argument : Must be one of <A, B, C>"-like error is raised.

UPDATE: Seems like list fields aren't actually the issue: The problem only appeared when using Union[float, List[float]] as a type annotation. Changing the type annotation to List[float] solved the issue.

johnatannvmd commented 2 years ago
@dataclass
class Train:
    """Example of a command to start a Training run."""
    # the training directory
    train_dir: Path = Path("~/train")

    def execute(self):
        print(f"Training in directory {self.train_dir}")

@dataclass
class Test:
    """Example of a command to start a Test run."""
    # the testing directory
    test_dir: Path = Path("~/train")

    def execute(self):
        print(f"Testing in directory {self.test_dir}")

@dataclass
class Program:
    """Available commands and common options"""
    command: Union[Train, Test]
    strlist: List[str] = field(default=None)  # list of the locations

    def execute(self):
        print(self.strlist)
        return self.command.execute()

if __name__ == "__main__":
    parser = ArgumentParser()
    parser.add_arguments(Program, dest="prog")
    args = parser.parse_args()
    prog: Program = args.prog
    prog.execute()

If you run this script with these parameters you will get those output where list does not parsed correctly

$ python run.py --strlist='test2 tes2t' train
['test2 tes2t']
Training in directory ~/train

Running script like this:

$ python run.py --strlist 'test2 tes2t' train

Gives me an error run.py: error: the following arguments are required: prog.command

How can I mix subparsers and list of string parameters:

parser.add_argument("--strlist", nargs="+", type=str, required=True)
MartinHowarth commented 1 year ago

The same problem applies to combining the add_config_path_arg=True with subparsers. I'm adding as a comment under this issue (rather than a new issue) because under the covers the config flag is added as a nargs="*" option. Let me know if you'd rather I raised as a separate issue.

The following invocation fails with RuntimeError: Unable to determine what function to use in order to load path deploy into a dictionary since the path's extension isn't registered in theextensions_to_loading_fndictionary. That's because it's trying to parse subcommand as a filename and the extension ("") isn't recognised.

python run.py --config config.yaml subcommand

but this works:

python run.py subcommand --config config.yaml 

Example code:

from dataclasses import dataclass

from simple_parsing import ArgumentParser

@dataclass
class Dummy:
    value: str

parser = ArgumentParser(add_config_path_arg=True)
parser.add_arguments(Dummy, dest="config")
subparsers = parser.add_subparsers()
subcommand = subparsers.add_parser("subcommand")
args = parser.parse_args()