Source code for _repobee.cli.argparse_ext

"""RepoBee extensions of argparse."""
import argparse

from typing import Optional

from _repobee import constants

__all__ = ["RepobeeParser", "OrderedFormatter"]


[docs]class RepobeeParser(argparse.ArgumentParser): """A thin wrapper around :py:class:`argparse.ArgumentParser`. The primary functionality of this class is to group the core CLI arguments into argument groups such that the CLI doesn't get too cluttered. """ def __init__(self, *args, is_core_command: bool = False, **kwargs): self._is_core_command = is_core_command super().__init__(*args, **kwargs) self._platform_args_grp = self.add_argument_group( title="platform arguments", description="Arguments related to the platform " "(e.g. GitHub or GitLab)", ) self._debug_args_grp = self.add_argument_group(title="debug arguments") self._alpha_args_grp = self.add_argument_group( title="alpha arguments", description="Arguments that are currently being trialed in alpha, " "and may change without further notice", )
[docs] def add_argument(self, *args, **kwargs): """Add an argument to this parser, placing it in an appropriate argument group. """ if not self._is_core_command: return super().add_argument(*args, **kwargs) platform_args = { "--token", "--org-name", "--template-org-name", "--user", "--base-url", } debug_args = {"--traceback", "--quiet"} alpha_args = {"--hook-results-file"} for arg in args: if arg in platform_args: return self._platform_args_grp.add_argument(*args, **kwargs) elif arg in debug_args: return self._debug_args_grp.add_argument(*args, **kwargs) elif arg in alpha_args: return self._alpha_args_grp.add_argument(*args, **kwargs) return super().add_argument(*args, **kwargs)
[docs] def add_argument_group( # type: ignore self, title: Optional[str] = None, description: Optional[str] = None ) -> argparse._ArgumentGroup: """Create a new argument group if the title does not exist, or return an existing one if it does. """ for grp in self._action_groups: if grp.title == title: if description is not None: grp.description = description return grp return super().add_argument_group(title, description)
[docs]class OrderedFormatter(argparse.HelpFormatter): """A formatter class for putting out the help section in a proper order. All of the arguments that are configurable in the configuration file should appear at the bottom (in arbitrary, but always the same, order). Any other arguments should appear in the order they are added. The internals of the formatter classes are technically not public, so this class is "unsafe" when it comes to new versions of Python. It may have to be disabled for future versions, but it works for 3.6, 3.7 and 3.8 at the time of writing. If this turns troublesome, it may be time to switch to some other CLI library. """
[docs] def add_arguments(self, actions): """Order actions by the name of the long argument, and then add them as arguments. The order is the following: [ NON-CONFIGURABLE | CONFIGURABLE | DEBUG ] Non-configurable arguments added without modification, which by default is the order they are added to the parser. Configurable arguments are added in the order defined by :py:const:`constants.ORDERED_CONFIGURABLE_ARGS`. Finally, debug commands (such as ``--traceback``) are added in arbitrary (but consistent) order. """ args_order = tuple( "--" + name.replace("_", "-") for name in constants.ORDERED_CONFIGURABLE_ARGS ) + ("--traceback",) def key(action): if len(action.option_strings) < 2: return -1 long_arg = action.option_strings[1] if long_arg in args_order: return args_order.index(long_arg) return -1 actions = sorted(actions, key=key) super().add_arguments(actions)