Attach a parser as a subcommand to a command at the specified path. :param subcommand_path: sequence of subcommand names leading to the parser that will host the new subcommand. An empty sequence indicates this parser. :param subcommand: name of the n
(
self,
subcommand_path: Iterable[str],
subcommand: str,
subcommand_parser: "Cmd2ArgumentParser",
**add_parser_kwargs: Any,
)
| 770 | return parser |
| 771 | |
| 772 | def attach_subcommand( |
| 773 | self, |
| 774 | subcommand_path: Iterable[str], |
| 775 | subcommand: str, |
| 776 | subcommand_parser: "Cmd2ArgumentParser", |
| 777 | **add_parser_kwargs: Any, |
| 778 | ) -> None: |
| 779 | """Attach a parser as a subcommand to a command at the specified path. |
| 780 | |
| 781 | :param subcommand_path: sequence of subcommand names leading to the parser that will |
| 782 | host the new subcommand. An empty sequence indicates this parser. |
| 783 | :param subcommand: name of the new subcommand |
| 784 | :param subcommand_parser: the parser to attach |
| 785 | :param add_parser_kwargs: additional arguments for the subparser registration (e.g. help, aliases) |
| 786 | :raises TypeError: if subcommand_parser is not an instance of the following or their subclasses: |
| 787 | 1. Cmd2ArgumentParser |
| 788 | 2. The parser_class configured for the target subcommand group |
| 789 | :raises ValueError: if the command path is invalid, doesn't support subcommands, or the |
| 790 | subcommand already exists |
| 791 | """ |
| 792 | if not isinstance(subcommand_parser, Cmd2ArgumentParser): |
| 793 | raise TypeError( |
| 794 | f"The attached parser must be an instance of 'Cmd2ArgumentParser' (or a subclass). " |
| 795 | f"Received: '{type(subcommand_parser).__name__}'." |
| 796 | ) |
| 797 | |
| 798 | target_parser = self.find_parser(subcommand_path) |
| 799 | subparsers_action = target_parser.get_subparsers_action() |
| 800 | |
| 801 | # Verify the parser is compatible with the 'parser_class' configured for this |
| 802 | # subcommand group. We use isinstance() here to allow for subclasses, providing |
| 803 | # more flexibility than the standard add_parser() factory approach which enforces |
| 804 | # a specific class. |
| 805 | if not isinstance(subcommand_parser, subparsers_action._parser_class): |
| 806 | raise TypeError( |
| 807 | f"The attached parser must be an instance of '{subparsers_action._parser_class.__name__}' " |
| 808 | f"(or a subclass) to match the 'parser_class' configured for this subcommand group. " |
| 809 | f"Received: '{type(subcommand_parser).__name__}'." |
| 810 | ) |
| 811 | |
| 812 | # Do not overwrite existing subcommands or aliases |
| 813 | all_names = (subcommand, *add_parser_kwargs.get("aliases", ())) |
| 814 | for name in all_names: |
| 815 | if name in subparsers_action._name_parser_map: |
| 816 | raise ValueError(f"Subcommand '{name}' already exists for '{target_parser.prog}'") |
| 817 | |
| 818 | # Use add_parser to register the subcommand name and any aliases |
| 819 | placeholder_parser = subparsers_action.add_parser(subcommand, **add_parser_kwargs) |
| 820 | |
| 821 | # To ensure accurate usage strings, recursively update 'prog' values |
| 822 | # within the injected parser to match its new location in the command hierarchy. |
| 823 | subcommand_parser.update_prog(placeholder_parser.prog) |
| 824 | |
| 825 | # Replace the parser created by add_parser() with our pre-configured one |
| 826 | subparsers_action._name_parser_map[subcommand] = subcommand_parser |
| 827 | |
| 828 | # Remap any aliases to our pre-configured parser |
| 829 | for alias in add_parser_kwargs.get("aliases", ()): |