Skip to content

Commit 9a20e7c

Browse files
committed
Added validation of subcommand handler attributes
1 parent 30d010f commit 9a20e7c

File tree

2 files changed

+17
-8
lines changed

2 files changed

+17
-8
lines changed

cmd2/cmd2.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -637,10 +637,14 @@ def _register_subcommands(self, cmdset: Union[CommandSet, 'Cmd']) -> None:
637637

638638
# iterate through all matching methods
639639
for method_name, method in methods:
640-
subcommand_name = getattr(method, constants.SUBCMD_ATTR_NAME)
640+
subcommand_name = getattr(method, constants.SUBCMD_ATTR_NAME) # type: str
641641
full_command_name = getattr(method, constants.SUBCMD_ATTR_COMMAND) # type: str
642642
subcmd_parser = getattr(method, constants.CMD_ATTR_ARGPARSER)
643643

644+
subcommand_valid, errmsg = self.statement_parser.is_valid_command(subcommand_name, is_subcommand=True)
645+
if not subcommand_valid:
646+
raise CommandSetRegistrationError('Subcommand {} is not valid: {}'.format(str(subcommand_name), errmsg))
647+
644648
command_tokens = full_command_name.split()
645649
command_name = command_tokens[0]
646650
subcommand_names = command_tokens[1:]

cmd2/parsing.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -277,14 +277,15 @@ def __init__(self,
277277
expr = r'\A\s*(\S*?)({})'.format(second_group)
278278
self._command_pattern = re.compile(expr)
279279

280-
def is_valid_command(self, word: str) -> Tuple[bool, str]:
280+
def is_valid_command(self, word: str, *, is_subcommand: bool = False) -> Tuple[bool, str]:
281281
"""Determine whether a word is a valid name for a command.
282282
283283
Commands can not include redirection characters, whitespace,
284284
or termination characters. They also cannot start with a
285285
shortcut.
286286
287287
:param word: the word to check as a command
288+
:param is_subcommand: Flag whether this command name is a subcommand name
288289
:return: a tuple of a boolean and an error string
289290
290291
If word is not a valid command, return ``False`` and an error string
@@ -297,18 +298,22 @@ def is_valid_command(self, word: str) -> Tuple[bool, str]:
297298
"""
298299
valid = False
299300

301+
if not isinstance(word, str):
302+
return False, 'must be a string. Received {} instead'.format(str(type(word)))
303+
300304
if not word:
301305
return False, 'cannot be an empty string'
302306

303307
if word.startswith(constants.COMMENT_CHAR):
304308
return False, 'cannot start with the comment character'
305309

306-
for (shortcut, _) in self.shortcuts:
307-
if word.startswith(shortcut):
308-
# Build an error string with all shortcuts listed
309-
errmsg = 'cannot start with a shortcut: '
310-
errmsg += ', '.join(shortcut for (shortcut, _) in self.shortcuts)
311-
return False, errmsg
310+
if not is_subcommand:
311+
for (shortcut, _) in self.shortcuts:
312+
if word.startswith(shortcut):
313+
# Build an error string with all shortcuts listed
314+
errmsg = 'cannot start with a shortcut: '
315+
errmsg += ', '.join(shortcut for (shortcut, _) in self.shortcuts)
316+
return False, errmsg
312317

313318
errmsg = 'cannot contain: whitespace, quotes, '
314319
errchars = []

0 commit comments

Comments
 (0)