remkop / picocli

Picocli is a modern framework for building powerful, user-friendly, GraalVM-enabled command line apps with ease. It supports colors, autocompletion, subcommands, and more. In 1 source file so apps can include as source & avoid adding a dependency. Written in Java, usable from Groovy, Kotlin, Scala, etc.

Home Page:https://picocli.info

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Setter method annotated by @Option with default value not been invoked when reusing it

JBWKZsf opened this issue · comments

Behavior: Setter method annotated by @option with default value not been invoked when reusing it(By saving reusing, I leveraged @Mixin).

For example:

I have a @Mixin class has bunch options

public class InputOptions {
    private final static String DEFAULT_ENV = "env";
    private final static String DEFAULT_REGION = "region";

    private String env;
    private String region;

    @CommandLine.Spec(MIXEE)
    private CommandSpec mixee;

    @CommandLine.Option(
        names = {"-e", "--env"},
        defaultValue = DEFAULT_ENV)
    public void setEnv(String env) {
        this.env = env;
        if (!DEFAULT_ENV.equals(env) && !this.equals(getRoot().inputOptions)) {
            getRoot().inputOptions.setEnv(env);
        }
    }

    @CommandLine.Option(
        names = {"-r", "--region"},
        defaultValue = DEFAULT_REGION)
    public void setRegion(String region) {
        this.region = region;
        if (!DEFAULT_REGION.equals(region) && !this.equals(getRoot().inputOptions)) {
            getRoot().inputOptions.setRegion(region);
        }
    }


    public String getEnv() {
        if (this.equals(getRoot().inputOptions)) return env;
        return getRoot().inputOptions.getEnv();
    }

    public String getRegion() {
        if (this.equals(getRoot().inputOptions)) return region;
        return getRoot().inputOptions.getRegion();
    }

    private T getRoot() {
        return (T) mixee.root().userObject();
    }
}

I reused this options in other commands(in parents and subcommands as global options, I referred example: https://picocli.info/#_use_case_configure_log_level_with_a_global_option)

But found the behavior that when I define the options, the parent command(parent/child are relevant to the mixee command level) 's @Mixin instance's option will not be invoked at all.

For example, I have command A(toplevel), B(subCommand), C(subsubCommand), all of them has declare d@Mixin InputOptions inputOptions.

Cases:

  1. Running A B -e XX C, setEnv in InputOptions of A is not called at all, but C's InputOptions setEnv in called.
  2. Running A B C -e XX, setEnv of InputOptions in both A and B is not called at all.

Worth to notice, setRegion has been called in all InputOptions instances in both 2 cases above.

Is this some misbehaving in picocli? I was expecting in any cases, the setters method should be called in all InputOptions options. Thus the default value can be set for the options.

Hi, sorry for the late response.
Can you run your program with tracing set to DEBUG (e.g. use system property -Dpicocli.trace=DEBUG)
and post the output here please?

Thank you for raising this. It looks like you have found a bug.

For the input A B C -e XX, the default value should be applied to setEnv for the A and the B commands, but as you say, this method is only called for C with the command line value "XX".

It looks like the bug is due to the implementation of equals on OptionSpec. As a result, the OptionSpec instance for the --env option in the mixin for A is considered equal to the instance in the B command and in the C command.
Since the --env option was assigned a value "XX" for the C command, it won't assign the default value any more.

The bug means that picocli thinks that the --env option instance in C is the same instance as in the A and B commands, so those are not assigned the default value either, which is incorrect.

Fixed in main. Thank you for raising this!