Skip to content

Commit 42371e6

Browse files
committed
Confirm attached parser is a Cmd2ArgumentParser-derived class.
1 parent bbfab98 commit 42371e6

4 files changed

Lines changed: 20 additions & 12 deletions

File tree

cmd2/argparse_custom.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -896,13 +896,21 @@ def attach_subcommand(
896896
:param subcommand: name of the new subcommand
897897
:param subcommand_parser: the parser to attach
898898
:param add_parser_kwargs: additional arguments for the subparser registration (e.g. help, aliases)
899-
:raises TypeError: if the subcommand parser's type does not match the 'parser_class' configured
900-
for the target subcommand group.
899+
:raises TypeError: if the subcommand parser is not an instance of 'Cmd2ArgumentParser'
900+
(or one of its subclasses), or if its type does not match the 'parser_class'
901+
configured for the target subcommand group.
901902
:raises ValueError: if the command path is invalid or doesn't support subcommands
902903
"""
904+
if not isinstance(subcommand_parser, Cmd2ArgumentParser):
905+
raise TypeError(
906+
f"The attached parser must be an instance of 'Cmd2ArgumentParser' (or a subclass). "
907+
f"Received: '{type(subcommand_parser).__name__}'."
908+
)
909+
903910
target_parser = self._find_parser(subcommand_path)
904911
subparsers_action = target_parser._get_subparsers_action()
905912

913+
# Mirror argparse's add_parser() behavior by requiring an exact type match with _parser_class
906914
if type(subcommand_parser) is not subparsers_action._parser_class:
907915
raise TypeError(
908916
f"The attached parser must be of type '{subparsers_action._parser_class.__name__}' "

cmd2/cmd2.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1189,8 +1189,9 @@ def attach_subcommand(
11891189
:param subcommand: name of the new subcommand
11901190
:param subcommand_parser: the parser to attach
11911191
:param add_parser_kwargs: additional arguments for the subparser registration (e.g. help, aliases)
1192-
:raises TypeError: if the subcommand parser's type does not match the 'parser_class' configured
1193-
for the target subcommand group.
1192+
:raises TypeError: if the subcommand parser is not an instance of 'Cmd2ArgumentParser'
1193+
(or one of its subclasses), or if its type does not match the 'parser_class'
1194+
configured for the target subcommand group.
11941195
:raises ValueError: if the command path is invalid or doesn't support subcommands
11951196
"""
11961197
root_parser, subcommand_path = self._get_root_parser_and_subcmd_path(command)

tests/test_argparse_custom.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -425,12 +425,17 @@ def test_subcommand_attachment_errors() -> None:
425425
with pytest.raises(ValueError, match="Subcommand 'fake' not found in 'root'"):
426426
root_parser.detach_subcommand([], "fake")
427427

428+
# Verify TypeError when attaching a non-Cmd2ArgumentParser type
429+
ap_parser = argparse.ArgumentParser(prog="non-cmd2-parser")
430+
with pytest.raises(TypeError, match=r"must be an instance of 'Cmd2ArgumentParser' \(or a subclass\)"):
431+
root_parser.attach_subcommand([], "sub", ap_parser) # type: ignore[arg-type]
432+
428433
# Verify TypeError when attaching a parser of a different type
429434
class SubParser(Cmd2ArgumentParser):
430435
pass
431436

432-
subclass_parser = SubParser(prog="sub")
433-
with pytest.raises(TypeError, match="The attached parser must be of type 'Cmd2ArgumentParser'"):
437+
subclass_parser = SubParser(prog="subclass")
438+
with pytest.raises(TypeError, match="to match the 'parser_class' configured for this subparsers action"):
434439
root_parser.attach_subcommand([], "sub", subclass_parser)
435440

436441

tests/test_cmd2.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4534,9 +4534,3 @@ def do_no_argparse(self, _statement: cmd2.Statement) -> None:
45344534
# Test command that doesn't use argparse
45354535
with pytest.raises(ValueError, match="Command 'no_argparse' does not use argparse"):
45364536
app.attach_subcommand("no_argparse", "sub", cmd2.Cmd2ArgumentParser())
4537-
4538-
# Test type mismatch
4539-
import argparse
4540-
4541-
with pytest.raises(TypeError, match="to match the 'parser_class' configured for this subparsers action"):
4542-
app.attach_subcommand("alias", "sub", argparse.ArgumentParser()) # type: ignore[arg-type]

0 commit comments

Comments
 (0)