@@ -406,18 +406,47 @@ def __init__(self, completekey: str = 'tab', stdin=None, stdout=None, *,
406
406
407
407
self ._register_subcommands (self )
408
408
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
+
409
429
def _autoload_commands (self ) -> None :
410
430
"""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
412
432
all_commandset_defs = CommandSet .__subclasses__ ()
413
433
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 )
421
450
422
451
def install_command_set (self , cmdset : CommandSet ) -> None :
423
452
"""
@@ -471,6 +500,7 @@ def install_command_set(self, cmdset: CommandSet) -> None:
471
500
if cmdset in self ._cmd_to_command_sets .values ():
472
501
self ._cmd_to_command_sets = \
473
502
{key : val for key , val in self ._cmd_to_command_sets .items () if val is not cmdset }
503
+ cmdset .on_unregister (self )
474
504
raise
475
505
476
506
def _install_command_function (self , command : str , command_wrapper : Callable , context = '' ):
0 commit comments