omni-us / jsonargparse

Implement minimal boilerplate CLIs derived from type hints and parse from command line, config files and environment variables
https://jsonargparse.readthedocs.io
MIT License
325 stars 49 forks source link

Selecting subtype from union type via command line. #588

Open MiguelMonteiro opened 1 month ago

MiguelMonteiro commented 1 month ago

🐛 Bug report

No way of selecting a subtype from a union type.

When adding an argument which is a union of two types there seems to be no direct way of specifying the desired type to be parsed/instantiated. This seems to be automatically detected from the argument names and values which is undesirable in scenarios where subtypes have similar arguments but different behaviors when instantiated.

To reproduce

from dataclasses import dataclass
from jsonargparse import ArgumentParser

@dataclass
class A:
    foo: int

@dataclass
class B:
    foo: int
    bar: float = 1.0

if __name__ == "__main__":
    parser = ArgumentParser(description="Example CLI", exit_on_error=False)
    parser.add_argument("--cfg", type=A | B)
    args = parser.parse_args(["--cfg.foo=1"])
    print(args)
    print(parser.instantiate_classes(args))

Expected behavior

A way of selecting the desired subtype from the command line.

Environment

mauvilsa commented 1 month ago

Thank you for reporting!

Explanation. Types inside Union are parsed from left to right. The first one that succeeds is chosen. In the example above since only foo is provided, then A succeeds and there is no attempt at B. Right now the only way is to specify bar so that A fails and it gets parsed as B.

Unfortunately this is not an easy fix. I do have in mind a big internal change that would fix this, which is needed to support other cases, e.g. #287. But this will take time.

MiguelMonteiro commented 1 month ago

Let me know if I can help, ideally it would be nice if the union type werer treated in the same way subclasses are, the first argument selects the type and defines the help string and then the following arguments get matched against the choice of class/type

mauvilsa commented 3 weeks ago

What I have in mind to do is described in https://github.com/omni-us/jsonargparse/issues/287#issuecomment-2433109024.