@@ -406,18 +406,47 @@ def __init__(self, completekey: str = 'tab', stdin=None, stdout=None, *,
406406
407407 self ._register_subcommands (self )
408408
409+ def find_commandsets (self , commandset_type : Type [CommandSet ], * , subclass_match : bool = False ) -> List [CommandSet ]:
410+ """
411+ Find all CommandSets that match the provided CommandSet type.
412+ By default, locates a CommandSet that is an exact type match but may optionally return all CommandSets that
413+ are sub-classes of the provided type
414+ :param commandset_type: CommandSet sub-class type to search for
415+ :param subclass_match: If True, return all sub-classes of provided type, otherwise only search for exact match
416+ :return: Matching CommandSets
417+ """
418+ return [cmdset for cmdset in self ._installed_command_sets
419+ if type (cmdset ) == commandset_type or (subclass_match and isinstance (cmdset , commandset_type ))]
420+
421+ def find_commandset_for_command (self , command_name : str ) -> Optional [CommandSet ]:
422+ """
423+ Finds the CommandSet that registered the command name
424+ :param command_name: command name to search
425+ :return: CommandSet that provided the command
426+ """
427+ return self ._cmd_to_command_sets .get (command_name )
428+
409429 def _autoload_commands (self ) -> None :
410430 """Load modular command definitions."""
411- # Search for all subclasses of CommandSet, instantiate them if they weren't provided in the constructor
431+ # Search for all subclasses of CommandSet, instantiate them if they weren't already provided in the constructor
412432 all_commandset_defs = CommandSet .__subclasses__ ()
413433 existing_commandset_types = [type (command_set ) for command_set in self ._installed_command_sets ]
414- for cmdset_type in all_commandset_defs :
415- init_sig = inspect .signature (cmdset_type .__init__ )
416- if not (cmdset_type in existing_commandset_types
417- or len (init_sig .parameters ) != 1
418- or 'self' not in init_sig .parameters ):
419- cmdset = cmdset_type ()
420- self .install_command_set (cmdset )
434+
435+ def load_commandset_by_type (commandset_types : List [Type ]) -> None :
436+ for cmdset_type in commandset_types :
437+ # check if the type has sub-classes. We will only auto-load leaf class types.
438+ subclasses = cmdset_type .__subclasses__ ()
439+ if subclasses :
440+ load_commandset_by_type (subclasses )
441+ else :
442+ init_sig = inspect .signature (cmdset_type .__init__ )
443+ if not (cmdset_type in existing_commandset_types
444+ or len (init_sig .parameters ) != 1
445+ or 'self' not in init_sig .parameters ):
446+ cmdset = cmdset_type ()
447+ self .install_command_set (cmdset )
448+
449+ load_commandset_by_type (all_commandset_defs )
421450
422451 def install_command_set (self , cmdset : CommandSet ) -> None :
423452 """
@@ -471,6 +500,7 @@ def install_command_set(self, cmdset: CommandSet) -> None:
471500 if cmdset in self ._cmd_to_command_sets .values ():
472501 self ._cmd_to_command_sets = \
473502 {key : val for key , val in self ._cmd_to_command_sets .items () if val is not cmdset }
503+ cmdset .on_unregister (self )
474504 raise
475505
476506 def _install_command_function (self , command : str , command_wrapper : Callable , context = '' ):
0 commit comments