diff --git a/CHANGES.md b/CHANGES.md index 9d209f767..db778de88 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,9 +10,11 @@ * ancestral, refine: Explicitly specify how the root and ambiguous states are handled during sequence reconstruction and mutation counting. [#1690][] (@rneher) * titers: Fix type errors in code associated with cross-validation of models. [#1688][] (@huddlej) +* Add help text to clarify difference in behavior between options that override defaults (e.g. `--metadata-delimiters`) vs. options that extend existing defaults (e.g. `--expected-date-formats`). [#1705][] (@victorlin) [#1688]: https://github.com/nextstrain/augur/pull/1688 [#1690]: https://github.com/nextstrain/augur/pull/1690 +[#1705]: https://github.com/nextstrain/augur/pull/1705 ## 27.0.0 (9 December 2024) diff --git a/augur/argparse_.py b/augur/argparse_.py index 6084fdd5e..291023172 100644 --- a/augur/argparse_.py +++ b/augur/argparse_.py @@ -1,25 +1,47 @@ """ Custom helpers for the argparse standard library. """ -from argparse import Action, ArgumentDefaultsHelpFormatter, ArgumentParser, _ArgumentGroup +from argparse import Action, ArgumentParser, _ArgumentGroup, HelpFormatter, SUPPRESS, OPTIONAL, ZERO_OR_MORE, _ExtendAction from typing import Union from .types import ValidationMode # Include this in an argument help string to suppress the automatic appending -# of the default value by argparse.ArgumentDefaultsHelpFormatter. This works -# because the automatic appending is conditional on the presence of %(default), -# so we include it but then format it as a zero-length string .0s. 🙃 +# of the default value by CustomHelpFormatter. This works because the +# automatic appending is conditional on the presence of %(default), so we +# include it but then format it as a zero-length string .0s. 🙃 # # Another solution would be to add an extra attribute to the argument (the -# argparse.Action instance) and then subclass ArgumentDefaultsHelpFormatter to -# condition on that new attribute, but that seems more brittle. +# argparse.Action instance) and then modify CustomHelpFormatter to condition +# on that new attribute, but that seems more brittle. # -# Copied from the Nextstrain CLI repo +# Initially copied from the Nextstrain CLI repo # https://github.com/nextstrain/cli/blob/017c53805e8317951327d24c04184615cc400b09/nextstrain/cli/argparse.py#L13-L21 SKIP_AUTO_DEFAULT_IN_HELP = "%(default).0s" +class CustomHelpFormatter(HelpFormatter): + """Customize help text. + + Initially copied from argparse.ArgumentDefaultsHelpFormatter. + """ + def _get_help_string(self, action: Action): + help = action.help + + if action.default is not None and action.default != []: + if isinstance(action, ExtendOverwriteDefault): + help += ' Specified values will override the default list.' + if isinstance(action, _ExtendAction): + help += ' Specified values will extend the default list.' + + if '%(default)' not in action.help: + if action.default is not SUPPRESS: + defaulting_nargs = [OPTIONAL, ZERO_OR_MORE] + if action.option_strings or action.nargs in defaulting_nargs: + help += ' (default: %(default)s)' + return help + + def add_default_command(parser): """ Sets the default command to run when none is provided. @@ -61,7 +83,7 @@ def add_command_subparsers(subparsers, commands, command_attribute='__command__' # Use the same formatting class for every command for consistency. # Set here to avoid repeating it in every command's register_parser(). - subparser.formatter_class = ArgumentDefaultsHelpFormatter + subparser.formatter_class = CustomHelpFormatter if not subparser.description and command.__doc__: subparser.description = command.__doc__ diff --git a/docs/conf.py b/docs/conf.py index 7b439c465..12b6e4f10 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -132,9 +132,10 @@ def prose_list(items): ("py:class", "json.decoder.JSONDecodeError"), ("py:class", "json.encoder.JSONEncoder"), - # This class can't be referenced. + # These classes can't be referenced. # ("py:class", "argparse._SubParsersAction"), + ("py:class", "argparse.HelpFormatter"), ] # -- Cross-project references ------------------------------------------------