Skip to content

Selecting subtype from union type via command line. #588

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
MiguelMonteiro opened this issue Oct 2, 2024 · 4 comments
Open

Selecting subtype from union type via command line. #588

MiguelMonteiro opened this issue Oct 2, 2024 · 4 comments
Labels
enhancement New feature or request

Comments

@MiguelMonteiro
Copy link
Contributor

🐛 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

  • jsonargparse version: 4.33.1
  • Python version: 3.10
  • How jsonargparse was installed: pip
  • OS: MacOS
@MiguelMonteiro MiguelMonteiro added the bug Something isn't working label Oct 2, 2024
@mauvilsa
Copy link
Member

mauvilsa commented Oct 5, 2024

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.

@mauvilsa mauvilsa added enhancement New feature or request and removed bug Something isn't working labels Oct 5, 2024
@MiguelMonteiro
Copy link
Contributor Author

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
Copy link
Member

What I have in mind to do is described in #287 (comment).

@trporn
Copy link

trporn commented Feb 23, 2025

I manually "solved" the above issue by adding to each dataclass a field with the name of the class so:

@dataclass
class A:
    foo: int
    A: bool 

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

I choose the correct class using --params.A=true or --params.B=true. While not ideal, this is a plausible way to choose among union types by explicitly naming the type required.
The problem with this hack is that I still cannot get the help for the given type. Given --params.A=true --help I would like to get help for type A with all of its fields (and the same for other types obviously). If this was somehow possible, it would make union types much more usable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants