groovy
groovy copied to clipboard
Fixed an issue with clibuilder not honoring the type flag GROOVY-9886
This change might be okay but possibly not needed. This currently works:
def cli = new CliBuilder()
cli.a(args: 1, longOpt: 'abc', type: double, 'abc')
def options = cli.parse(['--abc', '0.0'])
assert options.abc instanceof Double
CliBuilder doesn't use the type info capability offered by commons cli but instead performs its own conversion: https://github.com/apache/groovy/blob/master/subprojects/groovy-cli-commons/src/main/groovy/groovy/cli/commons/OptionAccessor.groovy#L118
Yea we probably do that if we don't need to access the type field, but currently we switch on the type info given by the option class. We're looking to move to pico cli in the future but currently this is breaking quite a lot of stuff.
Do you have an example of where you'd use the type?
In general, having code which relies on underlying implementation details is always harder to port compared to code which uses the published API.
I don't know whether the following helps:
def cli = new groovy.cli.commons.CliBuilder()
cli.a(args: 1, longOpt: 'abc', type: double, 'abc')
def options = cli.parse(['--abc', '0.0'])
assert options.abc instanceof Double
assert options.getSavedTypeOptions()['abc']?.type.toString() == 'double'
It illustrates how to get the type in a way the leaks some details of the CliBuilder implementation but not details from the underlying CLI library and should port unchanged across to picocli.
I can't really show the actual code, but it looks something like this
def a = cli.parse(..)
a.getOptions().each {
def prop = it.getLongOpt();
def type = it.getType();
def val = it.getValue()
switch (type) {
case Number:
parseNumber
break;
case Boolean:
boolean.valueof()
...
default:
throw new UnsupportedOperationException(...)
}
}
based on the groovy 2 docs, it does seem to support this behavior although didn't explicitly encourage it. http://docs.groovy-lang.org/2.4.7/html/gapi/groovy/util/CliBuilder.html#options
From that snippet, you are perhaps doing work you don't need to as most types are already handled. But if you really do need it, I would suggest using getSavedTypeOptions()
as per my previous example.
Ideally, if we're starting from scratch, we'll use that, but unfortunately the snippet can only run in groovy 3 and we're trying to migrate a lot of code written in groovy 2 to work with groovy 3. What's the solution that could work in both 2 and 3?
The examples I showed earlier work in 2.5+ but not 2.4. I don't know if that helps.
yea we're still on 2.4, 2.5 has it's own fair share of issues :-|
Interesting, if I switch to use groovy.util.CliBuilder
, should this work as well?
the following doesn't seem to work
def cli = new groovy.util.CliBuilder()
cli.a(args: 1, longOpt: 'abc', type: double, 'abc')
def options = cli.parse(['--abc', '0.0'])
assert options.abc instanceof Double
assert options.respondsTo("getSavedTypeOptions")
assert options.getSavedTypeOptions()['abc']?.type.toString() == 'double'
For the deprecated groovy.util.CliBuilder
, you'd need to change the last line to:
assert options.getDelegate().getSavedTypeOptions()['abc']?.type.toString() == 'double'
And that class (groovy.util
variant) is removed in Groovy 4 to allow for Java9+ JPMS compliance. So probably better to jump straight to other package variants.