Submodes

1. Problem Definition

Tools can have submodes that accept different groups of argument.

For example, the git command has a git commit submode and a git add submode etc. Each of these submodes accepts different arguments, but there are also some core options that are common to all submodes.

cfgclasses provides a parse_args_with_submodes() classmethod that can be used for tools with this setup. The common options which apply to all submodes are defined in a common options dataclass that is passed as the first argument to parse_args_with_submodes(), and the submode-specific options are defined in separate dataclass types for each submode. This method then returns both an instance of the common options dataclass and an instance of the submode-specific options dataclass.

2. Design Ammendments

The parse_args_with_submodes() classmethod is added. This functions signature is:

def parse_args_with_submodes(
    cls: Type[_T],
    argv: Sequence[str],
    submodes: dict[str, Type[_U]],
    prog: Optional[str] = None,
) -> tuple[_T, _U]:

Where _T is a type variable for the common options dataclass, and _U is a type variable that is bound to a common parent class for the submodes. The latter allows the submodes of the user to all inherit from a common base-type which can be beneficial for including methods on the configuration. For example, the Author likes to include a run() method on the configuration class to act as the main method of the tool. This method can be defined on the base-type and then overriden by all submodes to perform their custom flow for the appropriate submode.

The flow of this function follows the same flow as for the simpler parse_args() with the following additions:
  • The argparse.ArgumentParser.add_subparsers() method is used to add a new subparser for each submode with the appropriate name.

  • After the top-level dataclass has created its specification and added them to the parser, the same is done for each submode dataclass, adding the arguments to the appropriate subparser.

  • Once the arguments have been parsed into the argparse.Namespace, the submode is checked and an error raised if it is unselected.

  • The creation of the two dataclass instances is then performed as normal.

  • Validation is performed on both the top-level and submode dataclass instances.