Open arezaii opened 2 years ago
@arezaii —
Something not obvious to me (quite possibly due to lack of experience with argparse systems) is what the benefit of a number of predefined fields in the help/usage message is, as follows:
<pre-help>
<program name>
<about>
<author>
<version>
<usage>
rather than having a single multi-line <prefix>
string which could include any or all of these that the user want to print? Are there other contexts in which the distinct fields would be used individually, distinctly from this help message prefix?
Also, if one of these is left blank / undefined, do we get a blank line, or is the entire line suppressed?
Other comments:
In:
USAGE: ArgumentParserQuickstart <POSITIONAL> [-h, --help] [--debug] [--optional <OPTIONAL>]
ARGUMENTS:
POSITIONAL
OPTIONS:
to my eye, the all-caps headers look very old-school / man-page-y. The first thing I thought to look at to see what modern programs do was git
, whose default message when I type git
reads much more cleanly to me. The second was llvm
which is more all-caps-y like what you show here (though interestingly, gcc
, despite being older, is more lowercase-based). The third was brew
which is more in the git
camp. The fourth was spack
which is also more lower-case-y.
This makes me wonder whether we could identify programs that we'd consider to be good examples of modern usage/help messages that we'd want to model our choices after and use to guide answers to questions. That said, I'll take a stab at them all the same:
It is less clear how to represent arguments which expect a specific range, say 4..10 values, or exactly 8 values.
I think the user doesn't need to know the range indices, right, just the number of values? If that's correct, I'd probably come up with some way of indicating the number of arguments expected and keeping the indices to the implementation.
Should default values for arguments and options be displayed in the help output? In the usage message? If so, how/where?
I think they should definitely be available somewhere/somehow, but don't have an intuition as to where/how. What do other programs do? I think for chpl
, we had trouble fitting them into the --help
message in a way that didn't seem clunky, so we added a separate --help-settings
option to get the values (not saying that's a great solution, simply that it could be "via some other mechanism" if they didn't fit in the other outputs.
For flags/options with multiple possible identifiers, should they all be represented in the usage message? e.g. the usage message from the quickstart includes [-h, --help], should it just display [-h] there and the longer version in the help output under OPTIONS:?
It feels to me like printing both options in usage is overkill, but I'm also not sure whether I'd print just the shorter or longer versions. The longer versions obviously take more space, but are also more self-descriptive. I think mostly it seems to me that a program with a lot of options is probably going to need a manually curated usage line rather than an automatically generated one (which is not to say that the default one shouldn't do something reasonable, but I'm mentally comparing what an auto-generated usage would look like for chpl
in contrast to chpl --help
's usage line or man chpl
's 'synopsis'.
For parsers that operate on subcommands, how to give them the whole path from the parent?
No strong insight here.
...what the benefit of a number of predefined fields in the help/usage message is?
The goal here is to provide flexibility to the user to customize the message in part, without having to replace the entire message or losing out on the auto-generated parts.
For example, say I have a requirement to print out a short disclaimer before my help message, I can add that using pre-help
, but I will still get the benefit of having the argument parser generate the usage string (and potentially the program name, author, and version if we implement planned future work to read them from a .toml
file).
I can also take advantage of generated field headings, alignment and spacing without having to fiddle with the format of my input.
Also, if one of these is left blank / undefined, do we get a blank line, or is the entire line suppressed?
The entire line would be suppressed, so the help message takes up only the vertical space it needs based on the defined fields.
...to my eye, the all-caps headers look very old-school / man-page-y ... what modern programs do ...?
I didn't look at individual apps so much as other argument parsing libraries. Python has adopted the lowercase headings, but both Rust and Swift have maintained uppercase headings.
Here are some sample outputs from the other libraries I looked at:
Python:
$ PROG.py -h
usage: PROG [-h] [--foo [FOO]] bar [bar ...]
positional arguments:
bar bar help
options:
-h, --help show this help message and exit
--foo [FOO] foo help
Rust:
$ helptest -h
helptest
USAGE:
helptest [FLAGS]
FLAGS:
--config Some help text describing the --config arg
-h, --help Prints help information
-V, --version Prints version information
$ helptest --help
helptest
USAGE:
helptest [FLAGS]
FLAGS:
--config
The config file used by the myprog must be in JSON format
with only valid keys and may not contain other nonsense
that cannot be read by this program. Obviously I'm going on
and on, so I'll stop now.
-h, --help
Prints help information
-V, --version
Prints version information
Swift:
$ repeat --help
USAGE: repeat [--count <count>] [--include-counter] <phrase>
ARGUMENTS:
<phrase> The phrase to repeat.
OPTIONS:
--include-counter Include a counter with each repetition.
-c, --count <count> The number of times to repeat 'phrase'.
-h, --help Show help for this command.
The commonality is that it looks like we could improve readability by adding an extra line between each heading.
Apart from that, I prefer the look of the Swift output, but we can adapt/adopt any style we find reasonable.
It is less clear how to represent arguments which expect a specific range, say 4..10 values, or exactly 8 values.
I think the user doesn't need to know the range indices, right, just the number of values? If that's correct, I'd probably come up with some way of indicating the number of arguments expected and keeping the indices to the implementation.
Not sure if I got the right meaning. In the example, I was trying to express that the number of values is constrained by the range, so 4..10 expects at least 4 and at most 10 values and I am not sure how/if we should indicate that in the usage string. It may make more sense for the developer to choose to include that information in the help
text as opposed to us generating something like:
USAGE: myProg [--foo <foo> <foo> <foo> <foo> [foo ..10]]
I didn't find much in the way of precedent to follow for these types of indicators.
Should default values for arguments and options be displayed in the help output? In the usage message? If so, how/where?
I think they should definitely be available somewhere/somehow, but don't have an intuition as to where/how. What do other programs do?
There's mixed support for this. Python has an option to add them programmatically, but it appears to be all or nothing across every option/argument. Rust has the ability to specify the display property of a default value for every argument and sets them to display by default. Swift displays the defaults and it's not clear to me if this can be disabled.
A compromise might be leaving it up to the developer to add them to the help
message as they see fit. Perhaps a %default%
placeholder in the help string could indicate that we should print the default there, if at all. Initially, we may decide to leave it up to the developer to add the default values they want displayed into the help
message, with possible support for generated values in the future.
When the argument parser generates and displays help, how should it be formatted?
Depending on feedback here and on #18648, I was thinking of some format similar to this:
Other parsers give you some ability to modify the message format, with Rust having probably the most flexible/generous by way of custom templates. I'm not proposing anything that elegant, and initially the format would be fixed.
For a simple program like the quickstart example, the help output might look like:
There is still work to be done with pretty printing the help output with perfect column alignment and awareness of the screen size, this example is just using dumb tabs.
Usage Messages
For usage messages, I would propose ordering the parts something like this:
Where optional arguments or values are wrapped in
[ ]
and required ones are wrapped in< >
Subcommands are a little tricky because no individual subcommand can be required, but a program may require some selected subcommand to do anything useful. Ideally the main program should always support a call to
help
so technically the subcommand isn't required . Therefore, I propose that they are identified in the usage message as[SUBCOMMAND]
and have the help message list the available commands under theSUBCOMMANDS:
heading.There is a question of how multiple values should be indicated, and for simplicity I propose using
...
to indicate 'or more' values. Some output examples to demonstrate:[--flag <VAL>]
[--flag]
--flag <VAL ...>]
[--flag [VAL ...]]
[ ]
replaced with< >
It is less clear how to represent arguments which expect a specific range, say 4..10 values, or exactly 8 values.
Other questions:
Should default values for arguments and options be displayed in the help output? In the usage message? If so, how/where? This could be at the end of the help message for an argument/option, but not displayed at all in the case of flags and subcommands.
For flags/options with multiple possible identifiers, should they all be represented in the usage message? e.g. the usage message from the quickstart includes
[-h, --help]
, should it just display[-h]
there and the longer version in the help output underOPTIONS:
?For parsers that operate on subcommands, how to give them the whole path from the parent? That is, for a command like
mason add -h
, how to tell theadd
subcommand parser that the usage message should appendmason
? There could be a "parent" property on the argument parser, or that might be a reason to have separatebinaryName
andprogram
arguments, wherebinaryName
represents the path to the subcommand parser, whose name is represented inprogram
.