Skip to content
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

Mode for treating empty string the same as unspecified #2338

Open
nreid260 opened this issue Sep 16, 2024 · 5 comments
Open

Mode for treating empty string the same as unspecified #2338

nreid260 opened this issue Sep 16, 2024 · 5 comments

Comments

@nreid260
Copy link

This is similar in spirit to #1987

Basically, I want to be able to write CLIs such that explicitly passing empty-string as a param value is the same as having not specified the parameter at all. (e.g. --bar=x --foo= is the same as --bar=x). I suggest empty string because it's the closest thing BASH has to as "null" value. I see this as the best practice for optional parameters in any language.

In particular, I want support for default values to work correctly. For example:

@Option(name = "--my_enum") MyEnum myEnum = MyEnum.A
tool --my_enum=

would invoke tool with MyEnum.A as the value for myEnum

My objective is to make it easier to wrappers/scripts/etc to invoke tools with default values, without having to know what the default values are. Wrappers should be able to assume empty-string is the default value for every param.


Obviously this is a backward incompatible change, but could it be added as a setter on CommandLine?

@remkop
Copy link
Owner

remkop commented Sep 17, 2024

Hi @nreid260 , have you had a chance to read the section on Fallback values (and how they are different from Default values) in the picocli user manual? Hopefully that @fallbackValue annotation is what you are looking for.

If this doesn't meet your requirements, please take a look at the section on Custom Parameter Processing. There's an example use case in the "IParameterPreprocessor Parser Plugin" section that has some similarity to what you are describing, so you may be able to accomplish your use case with that approach.

@nreid260
Copy link
Author

I looked into both of these features and neither of them really capture my intent. Both of them could theoretically achieve the result, but they would be sort of clumbsy.

For fallback values, a string that parses into the desired default is needed. This isn't always possible (e.g. empty list of strings). Moreover, it's repetative, because the fallback value and default value are going to be very similar.

An IParameterPreprocessor could be written that does what I want, but it would need to be installed on every option/param separately. There's no way I can see to configure the entire command to use the same preprocessor. Also, though this isn't important for me, it might conflict with options that need some other preprocessor, besides handling empty-string.

@nreid260
Copy link
Author

Another case has come to mind: options that can be specified multiple times, with last-one-wins semantics. I'm not sure what the right behaviour would be for --foo=x --foo=

  1. Override x with the default value
  2. Ignore --foo= completely, so that x is used

@remkop
Copy link
Owner

remkop commented Sep 18, 2024

Another case has come to mind: options that can be specified multiple times, with last-one-wins semantics. I'm not sure what the right behaviour would be for --foo=x --foo=

  1. Override x with the default value
  2. Ignore --foo= completely, so that x is used

This can be controlled with CommandLine::setOverwrittenOptionsAllowed
(see https://picocli.info/#_overwriting_single_options ).

And in case 1: "Override x" it will be the fallbackValue that is used, not the default value.

@remkop
Copy link
Owner

remkop commented Sep 18, 2024

I can see how the current situation is not ideal.

Getting it to work is possible but not very elegant. I arrived at something like this:

public class Issue2338 {
    enum MyEnum { A, B, C}

    MyEnum myEnum;
    @CommandLine.Option(names = "--my_enum", defaultValue = "A", fallbackValue = "B", arity = "0..1")
    void setMyEnum(String value) {
        myEnum = MyEnum.valueOf(value.isEmpty() ? "B" : value);
    }
    public static void main(String[] args) {
        Issue2338 case1 = CommandLine.populateCommand(new Issue2338());
        Issue2338 case2 = CommandLine.populateCommand(new Issue2338(), "--my_enum");
        Issue2338 case3 = CommandLine.populateCommand(new Issue2338(), "--my_enum=");
        Issue2338 case4 = CommandLine.populateCommand(new Issue2338(), "--my_enum=C");

        System.out.printf("Option not specified;             myEnum=%s%n", case1.myEnum);
        System.out.printf("Option specified without value;   myEnum=%s%n", case2.myEnum);
        System.out.printf("Option specified as empty string; myEnum=%s%n", case3.myEnum);
        System.out.printf("Option specified with value;      myEnum=%s%n", case4.myEnum);
    }
}

This prints:

Option not specified;             myEnum=A
Option specified without value;   myEnum=B
Option specified as empty string; myEnum=B
Option specified with value;      myEnum=C

So, it is doable, but I can see it would be nice to have a more elegant solution.
My time to work on picocli is extremely limited though, and I don't see myself working on this in the near future...

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

No branches or pull requests

2 participants