@@ -516,7 +516,7 @@ def _SubParsersAction_remove_parser( # noqa: N802
516516 :raises ValueError: if the subcommand doesn't exist
517517 """
518518 if name not in self ._name_parser_map :
519- raise ValueError (f"Subcommand '{ name } ' not found " )
519+ raise ValueError (f"Subcommand '{ name } ' does not exist " )
520520
521521 subparser = self ._name_parser_map [name ]
522522
@@ -684,12 +684,12 @@ def update_prog(self, prog: str) -> None:
684684 # add_parser() will have the correct prog value.
685685 subparsers_action ._prog_prefix = self ._build_subparsers_prog_prefix (positionals )
686686
687- # subparsers_action.choices includes aliases. Since primary names are inserted first,
688- # we skip already updated parsers to ensure primary names are used in 'prog'.
687+ # subparsers_action._name_parser_map includes aliases. Since primary names are inserted
688+ # first, we skip already updated parsers to ensure primary names are used in 'prog'.
689689 updated_parsers : set [Cmd2ArgumentParser ] = set ()
690690
691691 # Set the prog value for each subcommand's parser
692- for subcmd_name , subcmd_parser in subparsers_action .choices .items ():
692+ for subcmd_name , subcmd_parser in subparsers_action ._name_parser_map .items ():
693693 if subcmd_parser in updated_parsers :
694694 continue
695695
@@ -707,9 +707,9 @@ def find_parser(self, subcommand_path: Iterable[str]) -> "Cmd2ArgumentParser":
707707 parser = self
708708 for name in subcommand_path :
709709 subparsers_action = parser .get_subparsers_action ()
710- if name not in subparsers_action .choices :
711- raise ValueError (f"Subcommand '{ name } ' not found in '{ parser .prog } '" )
712- parser = subparsers_action .choices [name ]
710+ if name not in subparsers_action ._name_parser_map :
711+ raise ValueError (f"Subcommand '{ name } ' does not exist for '{ parser .prog } '" )
712+ parser = subparsers_action ._name_parser_map [name ]
713713 return parser
714714
715715 def attach_subcommand (
@@ -729,7 +729,8 @@ def attach_subcommand(
729729 :raises TypeError: if subcommand_parser is not an instance of the following or their subclasses:
730730 1. Cmd2ArgumentParser
731731 2. The parser_class configured for the target subcommand group
732- :raises ValueError: if the command path is invalid or doesn't support subcommands
732+ :raises ValueError: if the command path is invalid, doesn't support subcommands, or the
733+ subcommand already exists
733734 """
734735 if not isinstance (subcommand_parser , Cmd2ArgumentParser ):
735736 raise TypeError (
@@ -751,6 +752,12 @@ def attach_subcommand(
751752 f"Received: '{ type (subcommand_parser ).__name__ } '."
752753 )
753754
755+ # Do not overwrite existing subcommands or aliases
756+ all_names = (subcommand , * add_parser_kwargs .get ("aliases" , ()))
757+ for name in all_names :
758+ if name in subparsers_action ._name_parser_map :
759+ raise ValueError (f"Subcommand '{ name } ' already exists for '{ target_parser .prog } '" )
760+
754761 # Use add_parser to register the subcommand name and any aliases
755762 placeholder_parser = subparsers_action .add_parser (subcommand , ** add_parser_kwargs )
756763
@@ -783,7 +790,7 @@ def detach_subcommand(self, subcommand_path: Iterable[str], subcommand: str) ->
783790 subparsers_action .remove_parser (subcommand ), # type: ignore[attr-defined]
784791 )
785792 except ValueError :
786- raise ValueError (f"Subcommand '{ subcommand } ' not found in '{ target_parser .prog } '" ) from None
793+ raise ValueError (f"Subcommand '{ subcommand } ' does not exist for '{ target_parser .prog } '" ) from None
787794
788795 def error (self , message : str ) -> NoReturn :
789796 """Override that applies custom formatting to the error message."""
0 commit comments