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

fallbackValue NULL_VALUE does not work for Collection options (was: How do I get picocli to parse "--item --item foo" as [null, "foo"] :List<String>) #1993

Closed
jiridanek opened this issue Apr 6, 2023 · 9 comments
Labels
theme: parser An issue or change related to the parser type: bug 🐛
Milestone

Comments

@jiridanek
Copy link

Originally asked at https://stackoverflow.com/questions/74273941/how-do-i-get-picocli-to-parse-item-item-foo-as-null-foo-listst. Thinking about it more, I decided that this is a missing feature, with the only possibility being the workaround in the SO answer.

I'd like to propose adding a feature to make what I want more straightforward!

I have a parameter named --msg-content-list-item

public class CliProtonJ2Sender implements Callable<Integer> {

    @CommandLine.Option(
        names = {"--msg-content-list-item"},
        arity = "0..1",
        defaultValue = CommandLine.Option.NULL_VALUE)
    private List<String> msgContentListItem;


    // ...
}

I wish for the following test to pass

    @Test
    void test_msgContentListItem() {
        CliProtonJ2Sender a = new CliProtonJ2Sender();
        CommandLine b = new CommandLine(a);
        CommandLine.ParseResult r = b.parseArgs("--msg-content-list-item", "--msg-content-list-item", "pepa");

        // https://github.com/powermock/powermock/wiki/Bypass-Encapsulation
        List<String> v = Whitebox.getInternalState(a, "msgContentListItem", a.getClass());
        assertThat(v).containsExactly(null, "pepa");
    }

But it doesn't and I get

missing (1): null
---
expected   : [null, pepa]
but was    : [pepa]

How do I define @CommandLine.Option to behave the way I want?

@remkop
Copy link
Owner

remkop commented Apr 6, 2023

Instead of defaultValue = CommandLine.Option.NULL_VALUE, please use fallbackValue = CommandLine.Option.NULL_VALUE.

The fallback value is what is used when the option is specified without parameter. The default value is what is applied when the option is not specified on the command line at all.

@jiridanek
Copy link
Author

Looking at the answer on SO, https://stackoverflow.com/a/74275000/1047788, it looks like I tried fallbackValue as well and it did not work for me either, so then I resorted to the workaround. I'll try this again to see if I can still confirm what I observed then. Thanks for your suggestion!

@jiridanek
Copy link
Author

@remkop As I worried, failbackValue does not work for me either

import org.junit.jupiter.api.Test;
import org.powermock.reflect.Whitebox;
import picocli.CommandLine;

import java.util.List;
import java.util.concurrent.Callable;

import static com.google.common.truth.Truth.assertThat;

@CommandLine.Command(
        name = "sender",
        mixinStandardHelpOptions = true,
        version = "1.0.0",
        description = "Opens AMQP connections"
)
public class MyMain implements Callable<Integer> {
    // e.g. `--item --item "someString"`
    @CommandLine.Option(names = {"--item"}, arity = "0..1", fallbackValue = CommandLine.Option.NULL_VALUE)
    private List<String> msgContentListItem;

    @Override
    public Integer call() throws Exception {
        return 0;
    }
}

class MyTests {
    MyMain main = new MyMain();
    CommandLine commandLine = new CommandLine(main);

    @Test
    void test_item__null() {
        commandLine.parseArgs("--item", "--item", "pepa");

        // https://github.com/powermock/powermock/wiki/Bypass-Encapsulation
        List<String> v = Whitebox.getInternalState(main, "msgContentListItem", main.getClass());
        assertThat(v).containsExactly(null, "pepa");
    }
}

prints

missing (1): null
---
expected   : [null, pepa]
but was    : [pepa]

	at MyTests.test_item__null(MyMain.java:56)

@remkop
Copy link
Owner

remkop commented Apr 6, 2023

Okay let me check.

@remkop
Copy link
Owner

remkop commented Apr 6, 2023

Away from PC, can you run with -Dpicocli.trace=DEBUG and show the output?

@jiridanek
Copy link
Author

@remkop

[picocli DEBUG] Creating CommandSpec for MyMain@3b07a0d6 with factory picocli.CommandLine$DefaultFactory
[picocli DEBUG] Creating CommandSpec for picocli.CommandLine$AutoHelpMixin@6c80d78a with factory picocli.CommandLine$DefaultFactory
[picocli INFO] Picocli version: 4.7.1, JVM: 17.0.2 (Oracle Corporation OpenJDK 64-Bit Server VM 17.0.2+8-86), OS: Linux 6.2.9-300.fc38.x86_64 amd64
[picocli INFO] Parsing 3 command line args [--item, --item, pepa]
[picocli DEBUG] Parser configuration: optionsCaseInsensitive=false, subcommandsCaseInsensitive=false, abbreviatedOptionsAllowed=false, abbreviatedSubcommandsAllowed=false, allowOptionsAsOptionParameters=false, allowSubcommandsAsOptionParameters=false, aritySatisfiedByAttachedOptionParam=false, atFileCommentChar=#, caseInsensitiveEnumValuesAllowed=false, collectErrors=false, endOfOptionsDelimiter=--, expandAtFiles=true, limitSplit=false, overwrittenOptionsAllowed=false, posixClusteredShortOptionsAllowed=true, separator=null, splitQuotedStrings=false, stopAtPositional=false, stopAtUnmatched=false, toggleBooleanFlags=false, trimQuotes=false, unmatchedArgumentsAllowed=false, unmatchedOptionsAllowedAsOptionParameters=true, unmatchedOptionsArePositionalParams=false, useSimplifiedAtFiles=false
[picocli DEBUG] (ANSI is disabled by default: systemproperty[picocli.ansi]=null, isatty=false, TERM=null, OSTYPE=null, isWindows=false, JansiConsoleInstalled=false, ANSICON=null, ConEmuANSI=null, NO_COLOR=null, CLICOLOR=null, CLICOLOR_FORCE=null)
[picocli DEBUG] Initializing command 'sender' (user object: MyMain@3b07a0d6): 3 options, 0 positional parameters, 0 required, 0 groups, 0 subcommands.
[picocli DEBUG] Set initial value for field java.util.List<String> MyMain.msgContentListItem of type interface java.util.List to null.
[picocli DEBUG] Set initial value for field boolean picocli.CommandLine$AutoHelpMixin.helpRequested of type boolean to false.
[picocli DEBUG] Set initial value for field boolean picocli.CommandLine$AutoHelpMixin.versionRequested of type boolean to false.
[picocli DEBUG] [0] Processing argument '--item'. Remainder=[--item, pepa]
[picocli DEBUG] '--item' cannot be separated into <option>=<option-parameter>
[picocli DEBUG] Found option named '--item': field java.util.List<String> MyMain.msgContentListItem, arity=0..1
[picocli DEBUG] Initializing binding for option '--item' (<msgContentListItem>) on MyMain@3b07a0d6 with empty List
[picocli DEBUG] [1] Processing argument '--item'. Remainder=[pepa]
[picocli DEBUG] '--item' cannot be separated into <option>=<option-parameter>
[picocli DEBUG] Found option named '--item': field java.util.List<String> MyMain.msgContentListItem, arity=0..1
[picocli DEBUG] 'pepa' doesn't resemble an option: 0 matching prefix chars out of 5 option names
[picocli INFO] Adding [pepa] to field java.util.List<String> MyMain.msgContentListItem for option --item on MyMain@3b07a0d6
[picocli DEBUG] Applying default values for command 'sender'
[picocli DEBUG] defaultValue not defined for field boolean picocli.CommandLine$AutoHelpMixin.helpRequested
[picocli DEBUG] defaultValue not defined for field boolean picocli.CommandLine$AutoHelpMixin.versionRequested

I'm thinking that the observations at the beginning of the answer https://stackoverflow.com/a/74275000/1047788 are relevant.

@remkop
Copy link
Owner

remkop commented Apr 6, 2023

You may have found a bug 🐛
(he reluctantly admitted 😅)

I'll investigate when I get to my pc.

@remkop remkop added this to the 4.7.2 milestone Apr 7, 2023
@remkop remkop added type: bug 🐛 theme: parser An issue or change related to the parser and removed type: question ❔ labels Apr 7, 2023
@remkop remkop changed the title How do I get picocli to parse "--item --item foo" as [null, "foo"] :List<String> fallbackValue NULL_VALUE does not work for Collection options (was: How do I get picocli to parse "--item --item foo" as [null, "foo"] :List<String>) Apr 7, 2023
@remkop
Copy link
Owner

remkop commented Apr 7, 2023

I confirmed this is a bug and have a fix in progress.

@remkop remkop closed this as completed in 2944add Apr 7, 2023
@remkop
Copy link
Owner

remkop commented Apr 7, 2023

Thank you for raising this.
I pushed a fix to the main branch.
This will be included in the upcoming 4.7.2 release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
theme: parser An issue or change related to the parser type: bug 🐛
Projects
None yet
Development

No branches or pull requests

2 participants