-
Notifications
You must be signed in to change notification settings - Fork 418
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
Parser configuration for convenience methods #561
Comments
Perhaps it makes sense to do this work together with #516, which also modifies the API of the convenience methods. |
This is a great idea! |
Thinking further, to future-proof this a little bit, it may be a good idea to use the builder pattern to let users create an execution configuration. An execution configuration is a set of attributes to customize the behaviour of the convenience methods. We know this configuration should include at least User code could look something like this:
New methods to add to
|
An alternative idea: introduce a new
Users can do all configuration on the
TBD Alternatively,
Some methods that only make sense on the
Without configuration, the shortest invocation is almost as brief as the 2.x-3.x convenience methods:
From picocli 4.0:
|
Jotting down some notes on the subject of exit codes for picocli 4.0: Spring BootSpring Boot has some good ideas for handling exit codes:
Current implementation (picocli 3.x)
GoalsDocumentationWhen generating CommandLine cmd = new CommandLine(new App());
// "success" includes usage help request, version info request, and successful command execution
cmd.setExitCodeForSuccess(0, "Successful program execution.");
// static mapping: simple and self-documentating
cmd.setExitCodeForException(ParameterException.class, 1, "Invalid user input");
cmd.setExitCodeForException(MissingParameterException.class, 2, "Missing required user input parameter");
cmd.setExitCodeForException(IOException.class, 3, "Incorrect file name or disk full");
// Dynamic mapping: more powerful but not self-documenting.
// Only useful when used with `execute`, not with `parse`, `parseArgs` or `populateCommand`.
cmd.setExitCodeExceptionMapper(ex -> {
if ("Bad data".equals(ex.getMessage())) { return 4; }
return 5;
}
);
// ... so document dynamic exit codes with API like this?
cmd.setExitCode(4, "Application encountered bad data");
cmd.setExitCode(5, "Unknown exception"); Dynamic Exit Status sectionIf any exit code is defined, picocli could automatically insert help section keys ExecutionAdd a new public static void main(String[] args) {
CommandLine cmd = new CommandLine(new App());
//... configure
int exitCode = cmd.execute(args);
System.exit(exitCode);
} This CommandLine cmd = new CommandLine(new App())
.setColorScheme(myColorScheme);
.setParseResultHandler(new RunLast());
.setExceptionHandler(new DefaultExceptionHandler<List<Object>>()); // TODO too verbose
int exitCode = cmd.execute(args); Exception handlingThe Implementationpublic int execute() {
int exitCode = 0;
IExitCodeGenerator exitCodeGenerator = new IExitCodeGenerator() {
public int getExitCode() { return getExitCodeForSuccess(); }
};
if (getCommandSpec().userObject() instanceof IExitCodeGenerator) {
exitCodeGenerator = (IExitCodeGenerator) getCommandSpec().userObject();
}
IExitCodeExceptionMapper exceptionMapper = new IExitCodeExceptionMapper() {
public int getExitCode(Throwable exception) {
Integer registered = exceptionMap.get(exception.getClass());
if (registered != null) { return registered; }
return getExitCodeExceptionMapper().getExitCode(exception); // never null
}
};
IExceptionHandler2<?> exceptionHandler = getExceptionHandler();
if (exceptionHandler instanceof AbstractHandler) { ((AbstractHandler) exceptionHandler).useColorScheme(getColorScheme()); }
ParseResult parseResult = null;
try {
parseResult = parseArgs(args);
IParseResultHandler2<?> handler = getParseResultHandler();
if (handler instanceof AbstractHandler) { ((AbstractHandler) handler).useColorScheme(getColorScheme()); }
handler.handleParseResult(parseResult);
exitCode = exitCodeGenerator.getExitCode();
} catch (ParameterException ex) {
try {
exitCode = exceptionMapper.getExitCode(ex);
exceptionHandler.handleParseException(ex, args);
} catch (Exception ex) {
ex.printStackTrace();
exitCode = exitCode == 0 ? 1 : exitCode;
}
} catch (ExecutionException ex) {
try {
exitCode = exceptionMapper.getExitCode(ex.getCause());
exceptionHandler.handleExecutionException(ex, parseResult);
} catch (Exception ex) {
ex.printStackTrace();
exitCode = exitCode == 0 ? 1 : exitCode;
}
}
return exitCode;
} Miscellaneous
|
…te` and `tryExecute` methods: configurable convenience methods with improved exit code support. * The new `execute` and `tryExecute` methods are similar to the `run`, `call` and `invoke` methods, but are not static, so they allow parser configuration. * In addition, these methods, in combination with the new `IExitCodeGenerator` and `IExitCodeExceptionMapper` interfaces, offer clean exit code support. * Finally, the `tryExecute` method rethrows any exception thrown from the Runnable, Callable or Method, while `execute` is guaranteed to never throw an exception. * Many variants of the previous `run`, `call` and `invoke` convenience methods are now deprecated in favor of the new `execute` methods. * Many methods on `AbstractHandler` are now deprecated. Still TODO: tests and documentation.
Initial version just landed in master. |
Todo: update javadoc to link these methods to each other:
|
The convenience methods
CommandLine::run
andCommandLine::call
don’t allow for the parser to be configured.Applications that need to parse with non-default parser settings can only use
CommandLine::parseWithHandlers
, which is more verbose and less readable.See #556 for an example.
One idea is to introduce overloaded
CommandLine::run
andCommandLine::call
methods that take aParserSpec
parameter used to config the parser.The text was updated successfully, but these errors were encountered: