Skip to content

Commit f216ed4

Browse files
committed
Make Cmd._command_parsers and Cmd._get_root_parser_and_subcmd_path() public.
1 parent 2a002fb commit f216ed4

6 files changed

Lines changed: 29 additions & 25 deletions

File tree

cmd2/cmd2.py

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -223,13 +223,17 @@ def __init__(self) -> None:
223223
DisabledCommand = namedtuple("DisabledCommand", ["command_function", "help_function", "completer_function"]) # noqa: PYI024
224224

225225

226-
class _CommandParsers:
226+
class CommandParsers:
227227
"""Create and store all command method argument parsers for a given Cmd instance.
228228
229229
Parser creation and retrieval are accomplished through the get() method.
230230
"""
231231

232232
def __init__(self, cmd: "Cmd") -> None:
233+
"""Initialize CommandParsers.
234+
235+
:param cmd: the Cmd instance whose parsers are being managed
236+
"""
233237
self._cmd = cmd
234238

235239
# Keyed by the fully qualified method names. This is more reliable than
@@ -620,7 +624,7 @@ def __init__(
620624
self.disabled_categories: dict[str, str] = {}
621625

622626
# Command parsers for this Cmd instance.
623-
self._command_parsers: _CommandParsers = _CommandParsers(self)
627+
self.command_parsers: CommandParsers = CommandParsers(self)
624628

625629
# Members related to printing asynchronous alerts
626630
self._alert_queue: deque[AsyncAlert] = deque()
@@ -935,7 +939,7 @@ def _install_command_function(self, command_func_name: str, command_method: Boun
935939
if not command_func_name.startswith(COMMAND_FUNC_PREFIX):
936940
raise CommandSetRegistrationError(f"{command_func_name} does not begin with '{COMMAND_FUNC_PREFIX}'")
937941

938-
# command_method must start with COMMAND_FUNC_PREFIX for use in self._command_parsers.
942+
# command_method must start with COMMAND_FUNC_PREFIX for use in self.command_parsers.
939943
if not command_method.__name__.startswith(COMMAND_FUNC_PREFIX):
940944
raise CommandSetRegistrationError(f"{command_method.__name__} does not begin with '{COMMAND_FUNC_PREFIX}'")
941945

@@ -1009,7 +1013,7 @@ def unregister_command_set(self, cmdset: CommandSet[Any]) -> None:
10091013
# Only remove the parser if this is the actual
10101014
# command since command synonyms don't own it.
10111015
if cmd_func_name == command_method.__name__:
1012-
self._command_parsers.remove(command_method)
1016+
self.command_parsers.remove(command_method)
10131017

10141018
if hasattr(self, COMPLETER_FUNC_PREFIX + command):
10151019
delattr(self, COMPLETER_FUNC_PREFIX + command)
@@ -1059,7 +1063,7 @@ def check_parser_uninstallable(parser: Cmd2ArgumentParser) -> None:
10591063
# We only need to check if it's safe to remove the parser if this
10601064
# is the actual command since command synonyms don't own it.
10611065
if cmd_func_name == command_method.__name__:
1062-
command_parser = self._command_parsers.get(command_method)
1066+
command_parser = self.command_parsers.get(command_method)
10631067
if command_parser is not None:
10641068
check_parser_uninstallable(command_parser)
10651069

@@ -1140,7 +1144,7 @@ def _unregister_subcommands(self, cmdset: CmdOrSet) -> None:
11401144
with contextlib.suppress(ValueError):
11411145
self.detach_subcommand(full_command_name, subcommand_name)
11421146

1143-
def _get_root_parser_and_subcmd_path(self, command: str) -> tuple[Cmd2ArgumentParser, list[str]]:
1147+
def get_root_parser_and_subcmd_path(self, command: str) -> tuple[Cmd2ArgumentParser, list[str]]:
11441148
"""Tokenize a command string and resolve the associated root parser and relative subcommand path.
11451149
11461150
This helper handles the initial resolution of a command string (e.g., 'foo bar baz') by
@@ -1170,7 +1174,7 @@ def _get_root_parser_and_subcmd_path(self, command: str) -> tuple[Cmd2ArgumentPa
11701174
if command_func is None:
11711175
raise ValueError(f"Root command '{root_command}' not found")
11721176

1173-
root_parser = self._command_parsers.get(command_func)
1177+
root_parser = self.command_parsers.get(command_func)
11741178
if root_parser is None:
11751179
raise ValueError(f"Command '{root_command}' does not use argparse")
11761180

@@ -1195,7 +1199,7 @@ def attach_subcommand(
11951199
2. The parser_class configured for the target subcommand group
11961200
:raises ValueError: if the command path is invalid or doesn't support subcommands
11971201
"""
1198-
root_parser, subcommand_path = self._get_root_parser_and_subcmd_path(command)
1202+
root_parser, subcommand_path = self.get_root_parser_and_subcmd_path(command)
11991203
root_parser.attach_subcommand(subcommand_path, subcommand, subcommand_parser, **add_parser_kwargs)
12001204

12011205
def detach_subcommand(self, command: str, subcommand: str) -> Cmd2ArgumentParser:
@@ -1207,7 +1211,7 @@ def detach_subcommand(self, command: str, subcommand: str) -> Cmd2ArgumentParser
12071211
:return: the detached parser
12081212
:raises ValueError: if the command path is invalid or the subcommand doesn't exist
12091213
"""
1210-
root_parser, subcommand_path = self._get_root_parser_and_subcmd_path(command)
1214+
root_parser, subcommand_path = self.get_root_parser_and_subcmd_path(command)
12111215
return root_parser.detach_subcommand(subcommand_path, subcommand)
12121216

12131217
@property
@@ -2448,7 +2452,7 @@ def _perform_completion(
24482452
else:
24492453
# There's no completer function, next see if the command uses argparse
24502454
func = self.get_command_func(command)
2451-
argparser = None if func is None else self._command_parsers.get(func)
2455+
argparser = None if func is None else self.command_parsers.get(func)
24522456

24532457
if func is not None and argparser is not None:
24542458
# Get arguments for complete()
@@ -4218,7 +4222,7 @@ def complete_help_subcommands(
42184222
return Completions()
42194223

42204224
# Check if this command uses argparse
4221-
if (func := self.get_command_func(command)) is None or (argparser := self._command_parsers.get(func)) is None:
4225+
if (func := self.get_command_func(command)) is None or (argparser := self.command_parsers.get(func)) is None:
42224226
return Completions()
42234227

42244228
completer = argparse_completer.DEFAULT_AP_COMPLETER(argparser, self)
@@ -4312,7 +4316,7 @@ def do_help(self, args: argparse.Namespace) -> None:
43124316
# Getting help for a specific command
43134317
func = self.get_command_func(args.command)
43144318
help_func = getattr(self, constants.HELP_FUNC_PREFIX + args.command, None)
4315-
argparser = None if func is None else self._command_parsers.get(func)
4319+
argparser = None if func is None else self.command_parsers.get(func)
43164320

43174321
# If the command function uses argparse, then use argparse's help
43184322
if func is not None and argparser is not None:

cmd2/decorators.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ def cmd_wrapper(*args: Any, **kwargs: Any) -> bool | None:
287287
)
288288

289289
# Pass cmd_wrapper instead of func, since it contains the parser info.
290-
arg_parser = cmd2_app._command_parsers.get(cmd_wrapper)
290+
arg_parser = cmd2_app.command_parsers.get(cmd_wrapper)
291291
if arg_parser is None:
292292
# This shouldn't be possible to reach
293293
raise ValueError(f"No argument parser found for {command_name}") # pragma: no cover

docs/features/argument_processing.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ stores internally. A consequence is that parsers don't need to be unique across
5050

5151
Since the `@with_argparser` decorator is making a deep-copy of the parser provided, if you wish
5252
to dynamically modify this parser at a later time, you need to retrieve this deep copy. This can
53-
be done using `self._command_parsers.get(self.do_commandname)`.
53+
be done using `self.command_parsers.get(self.do_commandname)`.
5454

5555
## Argument Parsing
5656

examples/scripts/save_help_text.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ def main() -> None:
8888
continue
8989

9090
cmd_func = self.get_command_func(item)
91-
parser = self._command_parsers.get(cmd_func)
91+
parser = self.command_parsers.get(cmd_func)
9292
if parser is None:
9393
continue
9494

tests/test_cmd2.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4115,10 +4115,10 @@ def test_startup_script_with_odd_file_names(startup_script) -> None:
41154115
def test_command_parser_retrieval(outsim_app: cmd2.Cmd) -> None:
41164116
# Pass something that isn't a method
41174117
not_a_method = "just a string"
4118-
assert outsim_app._command_parsers.get(not_a_method) is None
4118+
assert outsim_app.command_parsers.get(not_a_method) is None
41194119

41204120
# Pass a non-command method
4121-
assert outsim_app._command_parsers.get(outsim_app.__init__) is None
4121+
assert outsim_app.command_parsers.get(outsim_app.__init__) is None
41224122

41234123

41244124
def test_command_synonym_parser() -> None:
@@ -4128,8 +4128,8 @@ class SynonymApp(cmd2.cmd2.Cmd):
41284128

41294129
app = SynonymApp()
41304130

4131-
synonym_parser = app._command_parsers.get(app.do_synonym)
4132-
help_parser = app._command_parsers.get(app.do_help)
4131+
synonym_parser = app.command_parsers.get(app.do_synonym)
4132+
help_parser = app.command_parsers.get(app.do_help)
41334133

41344134
assert synonym_parser is not None
41354135
assert synonym_parser is help_parser
@@ -4481,7 +4481,7 @@ def do_root(self, _args: argparse.Namespace) -> None:
44814481
app = SubcmdApp()
44824482

44834483
# Verify root exists and uses argparse
4484-
root_parser = app._command_parsers.get(app.do_root)
4484+
root_parser = app.command_parsers.get(app.do_root)
44854485
assert root_parser is not None
44864486

44874487
# Attach child to root

tests/test_commandset.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -173,13 +173,13 @@ def do_builtin(self, _) -> None:
173173
app = WithCommandSets(command_sets=[cs])
174174

175175
# Make sure the synonyms have the same parser as what they alias
176-
builtin_parser = app._command_parsers.get(app.do_builtin)
177-
builtin_synonym_parser = app._command_parsers.get(app.do_builtin_synonym)
176+
builtin_parser = app.command_parsers.get(app.do_builtin)
177+
builtin_synonym_parser = app.command_parsers.get(app.do_builtin_synonym)
178178
assert builtin_parser is not None
179179
assert builtin_parser is builtin_synonym_parser
180180

181-
alias_parser = app._command_parsers.get(cmd2.Cmd.do_alias)
182-
alias_synonym_parser = app._command_parsers.get(app.do_alias_synonym)
181+
alias_parser = app.command_parsers.get(cmd2.Cmd.do_alias)
182+
alias_synonym_parser = app.command_parsers.get(app.do_alias_synonym)
183183
assert alias_parser is not None
184184
assert alias_parser is alias_synonym_parser
185185

@@ -190,7 +190,7 @@ def do_builtin(self, _) -> None:
190190
assert not hasattr(app, "do_alias_synonym")
191191

192192
# Make sure the alias command still exists, has the same parser, and works.
193-
assert alias_parser is app._command_parsers.get(cmd2.Cmd.do_alias)
193+
assert alias_parser is app.command_parsers.get(cmd2.Cmd.do_alias)
194194
out, _err = run_cmd(app, "alias --help")
195195
assert normalize(alias_parser.format_help())[0] in out
196196

0 commit comments

Comments
 (0)