Allow for substring matching on plugin names.

This commit is contained in:
Mike Auty
2017-11-14 11:04:47 +00:00
parent b075d8c31c
commit 51f508e602
+45 -1
View File
@@ -96,7 +96,7 @@ class CommandLine(object):
self.populate_requirements_argparse(parser, amagic.__class__)
configurables_list[amagic.__class__.__name__] = amagic
subparser = parser.add_subparsers(title = "Plugins", dest = "plugin")
subparser = parser.add_subparsers(title = "Plugins", dest = "plugin", action = HelpfulSubparserAction)
for plugin in plugin_list:
plugin_parser = subparser.add_parser(plugin, help = plugin_list[plugin].__doc__)
self.populate_requirements_argparse(plugin_parser, plugin_list[plugin])
@@ -246,6 +246,50 @@ class CommandLine(object):
required = not requirement.optional, **additional)
# We shouldn't really steal a private member from argparse, but otherwise we're just duplicating code
class HelpfulSubparserAction(argparse._SubParsersAction):
"""Class to either select a unique plugin based on a substring, or identity the alternatives"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# We don't want the action self-check to kick in, so we remove the choices list, the check happens in __call__
self.choices = None
def __call__(self, parser, namespace, values, option_string = None):
parser_name = values[0]
arg_strings = values[1:]
# set the parser name if requested
if self.dest is not argparse.SUPPRESS:
setattr(namespace, self.dest, parser_name)
matched_parsers = [name for name in self._name_parser_map if parser_name in name]
if len(matched_parsers) < 1:
msg = 'invalid choice {} (choose from {})'.format(parser_name, ', '.join(self._name_parser_map))
raise argparse.ArgumentError(self, msg)
if len(matched_parsers) > 1:
msg = 'plugin {} matches multiple plugins ({})'.format(parser_name, ', '.join(matched_parsers))
raise argparse.ArgumentError(self, msg)
parser = self._name_parser_map[matched_parsers[0]]
setattr(namespace, 'plugin', matched_parsers[0])
# parse all the remaining options into the namespace
# store any unrecognized options on the object, so that the top
# level parser can decide what to do with them
# In case this subparser defines new defaults, we parse them
# in a new namespace object and then update the original
# namespace for the relevant parts.
subnamespace, arg_strings = parser.parse_known_args(arg_strings, None)
for key, value in vars(subnamespace).items():
setattr(namespace, key, value)
if arg_strings:
vars(namespace).setdefault(argparse._UNRECOGNIZED_ARGS_ATTR, [])
getattr(namespace, argparse._UNRECOGNIZED_ARGS_ATTR).extend(arg_strings)
def progress_callback(progress, description = None):
""" A sinmple function for providing text-based feedback