github remkop/picocli v4.3.0
Picocli 4.3.0

latest releases: v4.7.6, v4.7.5, v4.7.4...
4 years ago

Picocli 4.3.0

The picocli community is pleased to announce picocli 4.3.0.

This is a fairly big release with 70 tickets closed, and over 50 bugfixes and enhancements. Many thanks to the picocli community who contributed 21 pull requests!

A major theme of this release is sharing options between commands:

  • New feature: "inherited" options. Options defined with scope = ScopeType.INHERIT are shared with all subcommands (and sub-subcommands, to any level of depth). Applications can define an inherited option on the top-level command, in one place, to allow end users to specify this option anywhere: not only on the top-level command, but also on any of the subcommands and nested sub-subcommands.
  • More powerful mixins. Mixin classes can declare a @Spec(MIXEE)-annotated field, and picocli will inject the CommandSpec of the command receiving this mixin (the "mixee") into this field. This is useful for mixins containing shared logic, in addition to shared options and parameters.

Another major theme is improved support for positional parameters:

  • Automatic indexes for positional parameters. Single-value positional parameters without an explicit index = "..." attribute are now automatically assigned an index based on the other positional parameters in the command. One use case is mixins with positional parameters.
  • Repeatable ArgGroups can now define positional parameters.

Other improvements:

  • The parser now supports case-insensitive mode for options and subcommands.
  • Error handlers now use ANSI colors and styles. The default styles are bold red for the error message, and italic for stack traces. Applications can customize with the new Help.ColorScheme methods errors and stackTraces.
  • The usage help message can now show an entry for -- in the options list with the @Command(showEndOfOptionsDelimiterInUsageHelp = true) annotation.
  • Easily make subcommands mandatory by making the top-level command a class that does not implement Runnable or Callable.

This is the sixty-eighth public release.
Picocli follows semantic versioning.

Table of Contents

  • New and noteworthy
    • Inherited Options
    • Case-insensitive mode
    • Automatic Indexes for Positional Parameters
    • Repeatable ArgGroups with Positional Parameters
    • @Spec(MIXEE) Annotation
    • Showing -- End of Options in usage help
  • Fixed issues
  • Deprecations
  • Potential breaking changes

New and Noteworthy

Inherited Options

This release adds support for "inherited" options. Options defined with scope = ScopeType.INHERIT are shared with all subcommands (and sub-subcommands, to any level of depth). Applications can define an inherited option on the top-level command, in one place, to allow end users to specify this option anywhere: not only on the top-level command, but also on any of the subcommands and nested sub-subcommands.

Below is an example where an inherited option is used to configure logging.

@Command(name = "app", subcommands = Sub.class)
class App implements Runnable {
    private static Logger logger = LogManager.getLogger(App.class);

    @Option(names = "-x", scope = ScopeType.LOCAL) // option is not shared: this is the default
    int x;
    
    @Option(names = "-v", scope = ScopeType.INHERIT) // option is shared with subcommands, sub-subcommands, etc
    public void setVerbose(boolean verbose) {
        // Configure log4j.
        // This is a simplistic example: you probably only want to modify the ConsoleAppender level.
        Configurator.setRootLevel(verbose ? Level.DEBUG : Level.INFO);
    }
    
    public void run() {
        logger.debug("-x={}", x);
    }
}

@Command(name = "sub")
class Sub implements Runnable {
    private static Logger logger = LogManager.getLogger(Sub.class);

    @Option(names = "-y")
    int y;
    
    public void run() {
        logger.debug("-y={}", y);
    }
}

Users can specify the -v option on either the top-level command or on the subcommand, and it will have the same effect.

# the -v option can be specified on the top-level command
java App -x=3 -v sub -y=4

Specifying the -v option on the subcommand will have the same effect. For example:

# specifying the -v option on the subcommand also changes the log level
java App -x=3 sub -y=4 -v

NOTE: Subcommands don't need to do anything to receive inherited options, but a potential drawback is that subcommands do not get a reference to inherited options.

Subcommands that need to inspect the value of an inherited option can use the @ParentCommand annotation to get a reference to their parent command, and access the inherited option via the parent reference.
Alternatively, for such subcommands, sharing options via mixins may be a more suitable mechanism.

Case-insensitive mode

By default, all options and subcommands are case sensitive. Case sensitivity can be switched off globally, as well as on a per-command basis.

To toggle case sensitivity for all commands, use the CommandLine::setSubcommandsCaseInsensitive and CommandLine::setOptionsCaseInsensitive methods. Use the CommandSpec::subcommandsCaseInsensitive and CommandSpec::optionsCaseInsensitive methods to give some commands a different case sensitivity than others.

Automatic Indexes for Positional Parameters

From this release, when the index = "..." attribute is omitted, the default index is index = "0+", which tells picocli to assign an index automatically, starting from zero, based on the other positional parameters defined in the same command.

A simple example can look like this:

class AutomaticIndex {
    @Parameters(hidden = true)  // "hidden": don't show this parameter in usage help message
    List<String> allParameters; // no "index" attribute: captures _all_ arguments

    @Parameters String group;    // assigned index = "0"
    @Parameters String artifact; // assigned index = "1"
    @Parameters String version;  // assigned index = "2"
}

Picocli initializes fields with the values at the specified index in the arguments array.

String[] args = { "info.picocli", "picocli", "4.3.0" };
AutomaticIndex auto = CommandLine.populateCommand(new AutomaticIndex(), args);

assert auto.group.equals("info.picocli");
assert auto.artifact.equals("picocli");
assert auto.version.equals("4.3.0");
assert auto.allParameters.equals(Arrays.asList(args));

The default automatic index (index = "0+") for single-value positional parameters is "anchored at zero": it starts at zero, and is increased with each additional positional parameter.

Sometimes you want to have indexes assigned automatically from a different starting point than zero. This can be useful when defining Mixins with positional parameters.

To accomplish this, specify an index with the anchor point and a + character to indicate that picocli should start to automatically assign indexes from that anchor point. For example:

class Anchored {
    @Parameters(index = "1+") String p1; // assigned index = "1" or higher
    @Parameters(index = "1+") String p2; // assigned index = "2" or higher
}

Finally, sometimes you want to have indexes assigned automatically to come at the end. Again, this can be useful when defining Mixins with positional parameters.

To accomplish this, specify an index with a + character to indicate that picocli should automatically assign indexes that come at the end. For example:

class Unanchored {
    @Parameters(index = "+") String penultimate; // assigned the penultimate index in the command
    @Parameters(index = "+") String last;        // assigned the last index in the command
}

Repeatable ArgGroups with Positional Parameters

From this release, positional parameters can be used in repeating Argument Groups.

When a @Parameters positional parameter is part of a group, its index is the index within the group, not within the command.

Below is an example of an application that uses a repeating group of positional parameters:

@Command(name = "grades", mixinStandardHelpOptions = true, version = "grades 1.0")
public class Grades implements Runnable {

    static class StudentGrade {
        @Parameters(index = "0") String name;
        @Parameters(index = "1") BigDecimal grade;
    }

    @ArgGroup(exclusive = false, multiplicity = "1..*")
    List<StudentGrade> gradeList;

    @Override
    public void run() {
        gradeList.forEach(e -> System.out.println(e.name + ": " + e.grade));
    }

    public static void main(String[] args) {
        System.exit(new CommandLine(new Grades()).execute(args));
    }
}

Running the above program with this input:

Alice 3.1 Betty 4.0 "X Æ A-12" 3.5 Zaphod 3.4

Produces the following output:

Alice: 3.1
Betty: 4.0
X Æ A-12: 3.5
Zaphod: 3.4

@Spec(MIXEE) Annotation

From this release, mixins are more powerful. Mixin classes can declare a @Spec(MIXEE)-annotated field, and picocli will inject the CommandSpec of the command receiving this mixin (the "mixee") into this field. This is useful for mixins containing shared logic, in addition to shared options and parameters.

Since picocli 4.3, the @Spec annotation has a value element.
The value is Spec.Target.SELF by default, meaning that the CommandSpec of the enclosing class is injected into the @Spec-annotated field.

For classes that are used as a mixins, there is another value that may be useful.
When @Spec(Spec.Target.MIXEE) is specified in a mixin class, the CommandSpec of the command receiving this mixin (the "mixee") is injected into the @Spec-annotated field.
This can be useful when a mixin contains logic that is common to many commands. For example:

import picocli.CommandLine.Option;
import picocli.CommandLine.Spec;

class AdvancedMixin {
    @Spec(Spec.Target.MIXEE) CommandSpec mixee;

    /**
     * When the -x option is specified on any subcommand,
     * multiply its value with another integer supplied by this subcommand
     * and set the result on the top-level command.
     * @param x the value of the -x option
     */
    @Option(names = "-x")
    void setValue(int x) {
        // Get another value from the command we are mixed into.
        // This mixin requires the command(s) it is mixed into to implement `IntSupplier`.
        int y = ((java.util.function.IntSupplier) mixee.userObject()).getAsInt();

        int product = x * y;

        // Set the result on the top-level (root) command.
        // This mixin requires the root command to implement `IntConsumer`.
        ((java.util.function.IntConsumer) mixee.root().userObject()).accept(product);
    }
}

Showing -- End of Options in usage help

From picocli 4.3, an entry for the -- End of Options delimiter can be shown in the options list of the usage help message of a command with the @Command(showEndOfOptionsDelimiterInUsageHelp = true) annotation.

Example command:

@Command(name = "myapp", showEndOfOptionsDelimiterInUsageHelp = true,
        mixinStandardHelpOptions = true, description = "Example command.")
class MyApp {
    @Parameters(description = "A file.") File file;
}

The usage help message for this command looks like this:

Usage: myapp [-hV] [--] <file>
Example command.
      <file>      A file.
  -h, --help      Show this help message and exit.
  -V, --version   Print version information and exit.
  --              This option can be used to separate command-line options from
                    the list of positional parameters.

Fixed issues

  • [#649][#948] Provide convenience API for inherited/global options (was: Feature request: inheriting mixins in subcommands). Thanks to Garret Wilson for the request and subsequent discussion (and patience!).
  • [#1001] Support required inherited options.
  • [#996] Default values should not be applied to inherited options.
  • [#985] API: Show end-of-options -- in usage help options list.
  • [#958] API: Add @Spec(Spec.Target.MIXEE) annotation element to allow mixins to get a reference to the command they are mixed into.
  • [#960] API: Add method CommandSpec::root to return the CommandSpec of the top-level command.
  • [#484][#845][#1008] API: Error handlers now use ANSI colors and styles. Added methods errors and stackTraces to Help.ColorScheme. Thanks to Neko Null for the pull request.
  • [#765][#1017] API: Added splitSynopsisLabel attribute on @Option and @Parameters for controlling how split regular expressions are displayed in the synopsis. Thanks to Murphy Han for the pull request and thanks to deining for raising this.
  • [#9][#1021][#1020][#1023][#154] API: Added support for case-insensitive subcommands and options. Thanks to NewbieOrange for the pull request, thanks to ifsheldon for exploring alternative solutions and helping clarify the requirements, and thanks to Neko Null for the pull request with documentation and executable examples.
  • [#564] Add support for relative indices for positional parameters. Useful in mixins and inherited positional parameters. Thanks to krisleonard-mcafee for raising this topic.
  • [#956] Enhancement: Default ParameterExceptionHandler should show stack trace when tracing is set to DEBUG level.
  • [#952] Enhancement: Make annotation processor quiet by default; add -Averbose annotation processor option to enable printing NOTE-level diagnostic messages to the console.
  • [#959] Enhancement: Print "Missing required subcommand" instead of throwing exception if command with subcommands does not implement Runnable or Callable. Thanks to Max Rydahl Andersen for the suggestion.
  • [#693][#1009][#1011] Enhancement: Add autocompletion for the built-in HelpCommand. Thanks to NewbieOrange for the pull request.
  • [#1022][#1029] Enhancement/Bugfix: Duplicate negated options were incorrectly accepted. Thanks to NewbieOrange for the pull request.
  • [#1030][#1029] Enhancement/Bugfix: setOptionsCaseInsensitive should make negatable options case insensitive. Thanks to NewbieOrange for the pull request.
  • [#1027][#1036] Enhancement: Support repeatable ArgGroups with positional parameters. Thanks to NewbieOrange for the pull request.
  • [#974] Enhancement/Bugfix: Add support for @ArgGroup argument groups in @Command-annotated methods. Thanks to Usman Saleem for raising this.
  • [#962][#961] Enhancement/Bugfix: Default value should only be applied if value is missing. Thanks to 粟嘉逸 and chirlo for raising this.
  • [#995][#1024][#1035] Enhancement/Bugfix: Reset multi-value options/positional params to initial value when reusing CommandLine instances. Thanks to Linyer-qwq, WU Jiangning, and Wycers for the pull request.
  • [#991][#993] Enhancement/Bugfix: Detecting terminal width fails on non-English Windows versions. Thanks to Stefan Gärtner for the pull request.
  • [#1040] Enhancement: internal code cleanup and minor fixes. Thanks to NewbieOrange for the pull request.
  • [#987] Bugfix: Bump JLine to 3.14.1 and fix [#969] autocompletion in Picocli Shell JLine3. Thanks to mattirn for the pull request.
  • [#969] Bugfix: Fixed broken autocompletion for nested subcommands in Picocli Shell JLine3. Thanks to niklas97 for raising this.
  • [#968] Bugfix: Avoid creating user object in Help constructor. Thanks to Immueggpain for raising this.
  • [#990] Bugfix: Options in subcommands were not reset to their initial value between invocations when the CommandLine object is reused. Thanks to marinier for pointing this out.
  • [#984][#997] Bugfix: Parameters heading is now shown in the usage help message when @filename is the only parameter. Thanks to Wycer for the pull request.
  • [#1004] Bugfix: Prevent NullPointerException in IParameterConsumer with @Option in @ArgGroup. Thanks to masupilami for raising this.
  • [#988][#1002] Bugfix: Option group sections in the usage help message now include subgroup options. Thanks to Wycer for the pull request.
  • [#957] Bugfix: Debug tracing now shows variable value instead of variable name.
  • [#955] Bugfix: TargetInvocationMessage handling in MethodBinding.set methods should use getTargetException not getCause; better error reporting.
  • [#1007] Bugfix: Custom Type Converters are missing for repeated subcommands. Thanks to Bastian Diehl for raising this.
  • [#1026] Bugfix: Hidden options should not impact usage help.
  • [#1034] Bugfix: Writer should flush() in UnmatchedArgumentException.printSuggestions. Thanks to darkmoonka for raising this.
  • [#963] DOC: Fixed broken link in README. Thanks to vladimirf7 for the pull request.
  • [#895] DOC: Added Initialization Before Execution section on initialization with subcommands to the user manual. Thanks to Walter Scott Johnson for raising this.
  • [#951] DOC: Fixed typo in picocli-codegen annotation processor documentation: disable.resource.config is correct (the option name was incorrectly spelled as disable.resources.config). Thanks to Max Rydahl Andersen for raising this.
  • [#966] DOC: Add section about Testing to the user manual.
  • [#973] DOC: Update documentation for using the picocli-codegen annotation processor during the build with Kotlin.
  • [#972] DOC: Add section "Handling Invalid Input" for custom type converters to user manual, demonstrating TypeConversionException. Add example InetSocketAddressConverter to picocli-examples. Thanks to Simon for raising this.
  • [#975] DOC: Update user manual Annotation Processor section to use ${project.groupId} instead of deprecated ${groupId}. Thanks to Dmitry Timofeev for the pull request.
  • [#976] DOC: Update user manual Testing section; add subsection on Testing Environment Variables. Thanks to David M. Carr for raising this and providing a sample project.
  • [#979][#981] DOC: Update user manual: add section Options with an Optional Parameter. Thanks to razvanh, Jake and mohdpasha for raising this.
  • [#989] DOC: Update examples for picocli-shell-jline3 prior to and after the [#987][#969] bugfix. Thanks to Ralf D. Müller for raising this.
  • [#998] DOC: Update manual: quote option parameter containing pipe characters in split regex for FIX message example. Thanks to Galder Zamarreño and Max Rydahl Andersen for raising this and subsequent discussion.
  • [#1012] DOC: Update user manual: add to ArgGroup limitations. Thanks to masupilami and patric-r for raising this and subsequent discussion.
  • [#1015] DOC: Update user manual: added section Variable Arity Options and Unknown Options. Thanks to Chris Smowton for raising this.
  • [#1019] DOC: Fix PrintExceptionMessageHandler example. Thanks to Adam Hosman for the pull request.
  • [#1006] DOC: Add Mixin example: MyLogger to the user manual.
  • [#1028][#1031] DOC: Update user manual: added Java 15 text blocks example. Thanks to Enderaoe for the pull request.
  • [#1037] DOC: Update user manual for programmatic API: fix typo. Thanks to Yoshida for the pull request.
  • [#1041] DOC: Fix broken links in javaDoc. Thanks to Andreas Deininger for the pull request.
  • [#1033] TEST: Added tests for [#984][#997]. Thanks to WU Jiangning for the pull request.
  • [#965] Dependency Upgrade: in picocli-examples, bump hibernate-validator from 6.0.2 to 6.1.2 to deal with CVE-2019-10219. Thanks to https://github.com/Security3rd for raising this.

Deprecations

No features were deprecated in this release.

Potential breaking changes

Behaviour has changed for some cases involving positional parameters.
One example is applications that define multiple positional parameters without an explicit index (see next section).
I hope these are edge cases.
Other than that, some error messages and details of the usage help message have changed.
See details below.

Default index for single-value positional parameters

Prior to picocli 4.3.0, if your application defines any single-value positional parameters without explicit index, these parameters would all point to index zero.
From picocli 4.3.0, picocli automatically assigns an index, so the first such parameter gets index 0 (zero), the next parameter gets index 1 (one), the next parameter gets index 2 (two), etc.

This may break applications that have multiple single-value positional parameters without explicit index, that expect to capture the first argument in all of these parameters.

Different error when user specifies too many parameters

The error message has changed when a user specifies more positional parameters than the program can accept. For example:

class SingleValue {
    @Parameters String str;
}

This program only accepts one parameter. What happens when this program is invoked incorrectly with two parameters, like this:

java SingleValue val1 val2

Before this release, picocli would throw an OverwrittenOptionException with message "positional parameter at index 0..* (<str>) should be specified only once".

From picocli 4.3, picocli throws an UnmatchedArgumentException with message "Unmatched argument at index 1: 'val2'".

This may break applications that have error handling that depends on an OverwrittenOptionException being thrown.

Different mechanism for dealing with too many parameters

Continuing with the previous example, before this release, applications could deal with this by allowing single-value options to be overwritten:

// before
CommandLine cmd = new CommandLine(new SingleValue());
cmd.setOverwrittenOptionsAllowed(true);
// ...

From picocli 4.3, applications need to allow unmatched arguments instead:

// after
CommandLine cmd = new CommandLine(new SingleValue());
cmd.setUnmatchedArgumentsAllowed(true);
// ...
// get the invalid values
cmd.getUnmatchedArguments();

Usage help message for single-value positional parameters

Before picocli 4.3.0, single-value positional parameters would incorrectly show an ellipsis (...) after their parameter label. This ellipsis is incorrect because it indicates that multiple values can be specified. The ellipsis is no longer shown for single-value positional parameters from picocli 4.3.0.

Before:

Usage: <main class> PARAM...
      PARAM...   Param description.

After:

Usage: <main class> PARAM
      PARAM   Param description.

This may break application tests that expect a specific usage help message format.

Different error for missing required options or parameters

Missing options list now starts with colon, no more square brackets

Before:

Missing required option '--required=<required>'
Missing required options [-a=<first>, -b=<second>, -c=<third>]

After:

Missing required option: '--required=<required>'
Missing required options: '-a=<first>', '-b=<second>', '-c=<third>'

Better message when both options and positional parameters are missing

Before:

Missing required options [-x=<x>, params[0]=<p0>, params[1]=<p1>]

After:

Missing required options and parameters: '-x=<x>', '<p0>', '<p1>'

Missing positional parameters are now quoted

Before:

Missing required parameter: <mandatory>
Missing required parameters: <mandatory>, <anotherMandatory>

After:

Missing required parameter: '<mandatory>'
Missing required parameters: '<mandatory>', '<anotherMandatory>'

Don't miss a new picocli release

NewReleases is sending notifications on new releases.