@@ -259,22 +259,6 @@ def __init__(self, completekey: str = 'tab', stdin=None, stdout=None, *,
259259 multiline_commands = multiline_commands ,
260260 shortcuts = shortcuts )
261261
262- # Load modular commands
263- self ._installed_command_sets = [] # type: List[CommandSet]
264- self ._cmd_to_command_sets = {} # type: Dict[str, CommandSet]
265- if command_sets :
266- for command_set in command_sets :
267- self .register_command_set (command_set )
268-
269- if auto_load_commands :
270- self ._autoload_commands ()
271-
272- # Verify commands don't have invalid names (like starting with a shortcut)
273- for cur_cmd in self .get_all_commands ():
274- valid , errmsg = self .statement_parser .is_valid_command (cur_cmd )
275- if not valid :
276- raise ValueError ("Invalid command name {!r}: {}" .format (cur_cmd , errmsg ))
277-
278262 # Stores results from the last command run to enable usage of results in a Python script or interactive console
279263 # Built-in commands don't make use of this. It is purely there for user-defined commands and convenience.
280264 self .last_result = None
@@ -412,6 +396,28 @@ def __init__(self, completekey: str = 'tab', stdin=None, stdout=None, *,
412396 # If False, then complete() will sort the matches using self.default_sort_key before they are displayed.
413397 self .matches_sorted = False
414398
399+ ############################################################################################################
400+ # The following code block loads CommandSets, verifies command names, and registers subcommands.
401+ # This block should appear after all attributes have been created since the registration code
402+ # depends on them and it's possible a module's on_register() method may need to access some.
403+ ############################################################################################################
404+ # Load modular commands
405+ self ._installed_command_sets = [] # type: List[CommandSet]
406+ self ._cmd_to_command_sets = {} # type: Dict[str, CommandSet]
407+ if command_sets :
408+ for command_set in command_sets :
409+ self .register_command_set (command_set )
410+
411+ if auto_load_commands :
412+ self ._autoload_commands ()
413+
414+ # Verify commands don't have invalid names (like starting with a shortcut)
415+ for cur_cmd in self .get_all_commands ():
416+ valid , errmsg = self .statement_parser .is_valid_command (cur_cmd )
417+ if not valid :
418+ raise ValueError ("Invalid command name {!r}: {}" .format (cur_cmd , errmsg ))
419+
420+ # Add functions decorated to be subcommands
415421 self ._register_subcommands (self )
416422
417423 def find_commandsets (self , commandset_type : Type [CommandSet ], * , subclass_match : bool = False ) -> List [CommandSet ]:
@@ -631,10 +637,14 @@ def _register_subcommands(self, cmdset: Union[CommandSet, 'Cmd']) -> None:
631637
632638 # iterate through all matching methods
633639 for method_name , method in methods :
634- subcommand_name = getattr (method , constants .SUBCMD_ATTR_NAME )
640+ subcommand_name = getattr (method , constants .SUBCMD_ATTR_NAME ) # type: str
635641 full_command_name = getattr (method , constants .SUBCMD_ATTR_COMMAND ) # type: str
636642 subcmd_parser = getattr (method , constants .CMD_ATTR_ARGPARSER )
637643
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+
638648 command_tokens = full_command_name .split ()
639649 command_name = command_tokens [0 ]
640650 subcommand_names = command_tokens [1 :]
0 commit comments