question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Parser configuration for convenience methods

See original GitHub issue

The convenience methods CommandLine::run and CommandLine::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 and CommandLine::call methods that take a ParserSpec parameter used to config the parser.

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Reactions:3
  • Comments:7 (6 by maintainers)

github_iconTop GitHub Comments

1reaction
remkopcommented, Apr 24, 2019

Initial version just landed in master.

1reaction
remkopcommented, Mar 29, 2019

Jotting down some notes on the subject of exit codes for picocli 4.0:

Spring Boot

Spring Boot has some good ideas for handling exit codes:

  • it won’t call System.exit itself, it just provides an API that returns an int. It is up to the application to call System.exit with that value.
  • it provides an interface ExitCodeGenerator. This solves the problem of deeply nested methods needing to return an exit code: there is no need to propagate this value through the call stack any more.
  • it provides a ExitCodeExceptionMapper interface that maps Exceptions to an exit code. Implementors can inspect details of the Exception object before deciding what exit code to return.

Current implementation (picocli 3.x)

  • Picocli currently has IResultHandler::andExit(int) and IExceptionHandler::andExit(int) methods that do call System.exit - deprecate this in picocli 4.0: these methods are hard to find, are not flexible enough to cover many use cases, and in general we should avoid calling System.exit in the library.

Goals

Documentation

When generating man page documentation (#299, #459), it would be nice to be able to include a section listing the EXIT STATUS values of the command (like man’s man page). This would require picocli to know all exit codes and their description.

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 section

If any exit code is defined, picocli could automatically insert help section keys SECTION_KEY_EXIT_STATUS_HEADING, SECTION_KEY_EXIT_STATUS before SECTION_KEY_FOOTER_HEADING, and put a IHelpSectionRenderer in the help section map that prints a list of the registered exit codes and their description.

Execution

Add a new execute method to CommandLine (or a subclass Executable?) that invokes the Runnable, Callable or Method and returns the exit code. This makes the application responsible for calling System.exit:

public static void main(String[] args) {
    CommandLine cmd = new CommandLine(new App());
    //... configure

    int exitCode = cmd.execute(args);
	System.exit(exitCode);
}

This execute method should delegate to the IParseResultHandler2 and IExceptionHandler2 (but would ignore their return value). Applications may customize runtime behaviour by supplying their own handlers, BUT custom handlers should really extend AbstractHandler so picocli can pass them the configured color scheme and OUT and ERR streams.

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 handling

The execute method would delegate error handling to the IExceptionHandler2, but an exception handler implementation may rethrow an exception, or extract the cause exception and rethrow that. If any exit codes are configured, the execute method should catch all exceptions, print their stack trace, and then return the appropriate error code.

Implementation

public 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

AbstractParseResultHandler needs an alternative for handleParseResult that takes a ColorScheme.

Read more comments on GitHub >

github_iconTop Results From Across the Web

XNI Parser Configuration - Apache Xerces
Parser configurations built using the Xerces Native Interface are made from a series of parser components. This document details the XNI API for...
Read more >
configparser — Configuration file parser — Python 3.11.1 ...
A convenience method which coerces the option in the specified section to a floating point number. See get() for explanation of raw, vars...
Read more >
Class Parser - Chevrotain
A data structure containing all the Tokens used by the Parser. Optional config: IParserConfig. The Parser's configuration. Returns Parser ...
Read more >
Writing Parser Extensions - GitHub Pages
Implementing a custom parser is one of two possible ways to extend the syntax of a ... Directives and in many cases that...
Read more >
YangParserTestUtils (yangtools-docs 7.0.3 API) - javadoc.io
Utility class which provides convenience methods for producing effective schema ... Parameters: config - parser configuration: files - YANG files to be ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found