Allow adding help messages to command line arguments
See original GitHub issueš Feature Request
It would be nice to be able to add help messages (similar to the argparse library) to command line arguments defined in .yaml configs / structured configs.
These help messages should be available to view conveniently when typing python myapp.py --help
into the command line so that the programās user doesnāt have to look through the config files and / or the rest of the code to figure out what some command line arguments do.
Motivation
Is your feature request related to a problem? Please describe.
It would be nice if users of programs written with hydra could see helpful messages for command line arguments.
Itās already available as a feature in Pythonās argparse
library by passing a help
parameter containing a help message to parser.add_argument()
as demonstrated here.
Adding the ability to add help messages to command line arguments in hydra may help to improve a userās understanding of what a program does.
Pitch
Describe the solution youād like
The solution should:
-
Allow a developer using Hydra to define a help message for each command line argument and each group defined in .yaml files or structured configs.
-
Allow an end user of a program written with Hydra to see what command line arguments can be passed as well as a description associated with each of them.
The solution could also optionally have the following features:
-
It would also be nice if the defaults for each attribute were optionally shown to the user in the help message. An
argparse
equivalent is shown here. -
It would also be nice if the type of each attribute were optionally shown in the help message (if known, such as when using structured configs). An
argparse
equivalent is shown here.
Describe alternatives youāve considered
I have a few suggestions on how this could be implemented (1-4 below) in order of increasing plausibility. Suggestions 1-2 have possible solutions for both .yaml files and structured configs. Suggestions 3-4 have better suggestions for structured configs.
I feel like suggestion 4 might be the best for structured configs. Iām not sure if help messages could be defined better in .yaml files.
1. How it could be done currently without any changes
At the moment, I understand that help messages for hydra can be configured as per this tutorial (repeated below):
hydra:
help:
# App name, override to match the name your app is known by
app_name: ${hydra.job.name}
# Help header, customize to describe your app to your users
header: |
${hydra.help.app_name} is powered by Hydra.
footer: |
Powered by Hydra (https://hydra.cc)
Use --hydra-help to view Hydra specific help
# Basic Hydra flags:
# $FLAGS_HELP
#
# Config groups, choose one of:
# $APP_CONFIG_GROUPS: All config groups that does not start with hydra/.
# $HYDRA_CONFIG_GROUPS: All the Hydra config groups (starts with hydra/)
#
# Configuration generated with overrides:
# $CONFIG : Generated config
#
template: |
${hydra.help.header}
== Configuration groups ==
Compose your configuration from those groups (group=option)
$APP_CONFIG_GROUPS
== Config ==
Override anything in the config (foo.bar=value)
$CONFIG
${hydra.help.footer}
e.g. Itās possible to add help messages in manually by overriding hydra.help.template
as needed. This could be done by manually typing the desired result of python my_app.py --help
. But, this seems counterproductive (e.g. what if something is updated in db.mysql in the tutorialās example? Itās possible to easily forget to update any manual help messages in hydra.help.template
!).
Instead, perhaps help messages could be defined at the same time and place as the associated config attributes. So, hereās my next alternative suggestion:
2. Parse comments in .yaml files / structured configs
As inspiration for this alternative example, I happened to come across the next Hydra versionās doc site. I found a section on the āNevergrad Sweeper Pluginā example here.
The comments in the example (kind of) felt like I could be reading the help text that would appear for this config when using python my_app.py --help
. So, a potential quick workaround could be to parse comments directly above a config attribute as help text.
Example of using this for a config.yaml file (adapted from the Hydra tutorial website):
# Group to decide what type of database to use
db:
# Driver to use for the database
driver: mysql
# Username for the database
user: omry
# Password for the database
pass: secret
Example of using this for structured configs (adapted from the Hydra tutorial website):
@dataclass
class MySQLConfig:
# Driver to use for the database
driver: str = "mysql"
# Username for the database
user: str = "omry"
# Password for the database
pass: str = "secret"
@dataclass
class Config(DictConfig):
# Group to decide what type of database to use
db: MySQLConfig = MISSING
When a user types python myapp.py --help
, Iād expect the following help message to appear (adapted from the Hydra tutorial website):
my_app is powered by Hydra.
== Configuration groups ==
Compose your configuration from those groups (group=option)
db: mysql - Group to decide what type of database to use
== Config ==
Override anything in the config (foo.bar=value)
db: - Group to decide what type of database to use
driver: mysql - Driver to use for the database
user: omry - Username for the database
pass: secret - Password for the database
Powered by Hydra (https://hydra.cc)
Use --hydra-help to view Hydra specific help
(Sorry the indentation is all weird on the help text - this seems to be a āfeatureā of GitHub)
This method, although easy to implement, feels very āhackyā. Especially for the structured configs as itās just Python comments that Iāve used in this alternative suggestion.
3. Define a @dataclass
helper function
Perhaps, in the case of structured configs at least, a helper function could be used to define help text. e.g.:
@dataclass
class MySQLConfig:
driver: str = "mysql"
user: str = "omry"
pass: str = "secret"
def help(self) -> Dict[str, str]:
helpDict = {
"driver": "Driver to use for the database",
"user": "Username for the database",
"pass": "Password for the database"
}
return helpDict
@dataclass
class Config(DictConfig):
db: MySQLConfig = MISSING
def help(self) -> Dict[str, str]:
helpDict = {"db": "Group to decide what type of database to use"}
return helpDict
This could work if you define that help()
should be a method defined by structured configs to add help messages.
However, this has the disadvantage that if the structured config class is large, then it would get hard to check if every attribute that you want to have a help message has a help message in helpDict
.
This should output the same help message as in the previous suggestion.
4. Using @dataclass
ās metadata
parameter
The dataclasses.field()
method has a metadata
parameter. The definition provided by the Python docs is:
metadata: This can be a mapping or None. None is treated as an empty dict. This value is wrapped in MappingProxyType() to make it read-only, and exposed on the Field object. It is not used at all by Data Classes, and is provided as a third-party extension mechanism. Multiple third-parties can each have their own key, to use as a namespace in the metadata.
This seems like the best solution to implement (for structured configs at least) for the following reasons:
-
metadata
is read-only -
Each attributeās help message is defined at the location the attribute is defined which makes it easy to check if a help message has been written for a given attribute.
-
ā
metadata
is provided as a third-party extension mechanismā - This is exactly the use case! -
Itās less lines of code compared to my other structured config examples (see below)
An example of how this could be used is to suggest to a Hydra developer to use the keyword "help"
as a metadata dictionary key to a help message for each attribute that the developer wants to assign a help message.
@dataclass
class MySQLConfig:
driver: str = field(default="mysql", metadata={"help", "Driver to use for the database"})
user: str = field(default="omry", metadata={"help", "Username for the database"})
pass: str = field(default="secret", metadata={"help", "Password for the database"})
@dataclass
class Config(DictConfig):
db: MySQLConfig = field(default=MISSING, metadata={"help", "Group to decide what type of database to use"})
Again, this should output the same help message as in the previous suggestion.
Iām sorry that this feature request is so long, I just kept thinking of alternative ways the solution could possibly be implemented.
That being said, of course, none of these suggested implementations are necessarily the best - these are just suggestions! š
If somethingās not clear please let me know!
Are you willing to open a pull request? (See CONTRIBUTING)
No, sorry. š
Additional context
Add any other context or screenshots about the feature request here.
Issue Analytics
- State:
- Created 3 years ago
- Reactions:38
- Comments:7 (2 by maintainers)
Top GitHub Comments
Just chiming in with support for this feature. Help messages & a self-documenting CLI are my favorite feature of
click
&argparse
, so the lack of them is a source of hesitation when considering Hydra for projects (along with the inability to use Hydra andclick
/argparse
together in the same program #409 , which would be a reasonable compromise in many situations).simple-parsing - https://github.com/lebrice/SimpleParsing (an argparse library using dataclass) supports help well.
It uses both docstring and field comments like this: