def add_subparsers(parser, cli_name, dest, command_extensions, hide_extensions=None, required=True): """ Create argparse subparser for each extension. The ``cli_name`` is used for the title and description of the ``add_subparsers`` function call. For each extension a subparser is created. If the extension has an ``add_arguments`` method it is being called. :param parser: the parent argument parser :type parser: :py:class:`argparse.ArgumentParser` :param str cli_name: name of the command line command to which the subparsers are being added :param str dest: name of the attribute under which the selected extension will be stored :param dict command_extensions: dict of command extensions by their name where each contributes a command with specific arguments """ # add subparser with description of available subparsers description = '' if command_extensions: max_length = max( len(name) for name in command_extensions.keys() if hide_extensions is None or name not in hide_extensions) for name in sorted(command_extensions.keys()): if hide_extensions is not None and name in hide_extensions: continue extension = command_extensions[name] description += '%s %s\n' % (name.ljust(max_length), get_first_line_doc(extension)) metavar = 'Call `{cli_name} <command> -h` for more detailed ' \ 'usage.'.format_map(locals()) subparser = parser.add_subparsers(title='Commands', description=description, metavar=metavar) # use a name which doesn't collide with any argument # but is readable when shown as part of the the usage information subparser.dest = ' ' + dest.lstrip('_') subparser.required = required # add extension specific sub-parser with its arguments for name in sorted(command_extensions.keys()): extension = command_extensions[name] command_parser = subparser.add_parser( extension.NAME, description=get_first_line_doc(extension), formatter_class=argparse.RawDescriptionHelpFormatter) command_parser.set_defaults(**{dest: extension}) if hasattr(extension, 'add_arguments'): extension.add_arguments(command_parser, '{cli_name} {name}'.format_map(locals())) return subparser
def print_entry_point(self, args, dist, entry_point): exception = None try: plugin = entry_point.load() except Exception as e: if not args.all: # skip entry points which failed to load return exception = e plugin = None else: try: plugin() except Exception as e: if not args.all: # skip plugins which failed to be instantiated return exception = e prefix = ' ' if exception is None else '-' print(prefix, entry_point.name + ':', get_first_line_doc(plugin)) if args.verbose: print(prefix, ' ', 'module_name:', entry_point.module_name) if entry_point.attrs: print(prefix, ' ', 'attributes:', '.'.join(entry_point.attrs)) print(prefix, ' ', 'distribution:', repr(dist)) if exception: print(prefix, ' ', 'reason:', str(exception))
def print_extension_point(self, args, name, entry_point): exception = None try: extension_point = entry_point.load() except Exception as e: if not args.all: # skip entry points which failed to load return exception = e extension_point = None prefix = '' if exception is None else '- ' print(prefix + name + ':', get_first_line_doc(extension_point)) if args.verbose: print(prefix, ' ', 'module_name:', entry_point.module_name) if entry_point.attrs: print(prefix, ' ', 'attributes:', '.'.join(entry_point.attrs)) if hasattr(extension_point, 'EXTENSION_POINT_VERSION'): print(prefix, ' ', 'version:', extension_point.EXTENSION_POINT_VERSION) if exception: print(prefix, ' ', 'reason:', str(exception))
def add_subparsers_on_demand(parser, cli_name, dest, group_name, hide_extensions=None, required=True, argv=None): """ Create argparse subparser for each extension on demand. The ``cli_name`` is used for the title and description of the ``add_subparsers`` function call. For each extension a subparser is created is necessary. If no extension has been selected by command line arguments all first level extension must be loaded and instantiated. If a specific extension has been selected by command line arguments the sibling extension can be skipped and only that one extension (as well as potentially its recursive extensions) are loaded and instantiated. If the extension has an ``add_arguments`` method it is being called. :param parser: the parent argument parser :type parser: :py:class:`argparse.ArgumentParser` :param str cli_name: name of the command line command to which the subparsers are being added :param str dest: name of the attribute under which the selected extension will be stored :param str group_name: the name of the ``entry_point`` group identifying the extensions to be added :param list hide_extensions: an optional list of extension names which should be skipped :param bool required: a flag if the command is a required argument :param list argv: the list of command line arguments (default: ``sys.argv``) """ # add subparser without a description for now mutable_description = MutableString() subparser = parser.add_subparsers( title='Commands', description=mutable_description, metavar=f'Call `{cli_name} <command> -h` for more detailed usage.') # use a name which doesn't collide with any argument # but is readable when shown as part of the the usage information subparser.dest = ' ' + dest.lstrip('_') subparser.required = required # add entry point specific sub-parsers but without a description and # arguments for now entry_points = get_entry_points(group_name) command_parsers = {} for name in sorted(entry_points.keys()): command_parser = subparser.add_parser( name, formatter_class=argparse.RawDescriptionHelpFormatter) command_parsers[name] = command_parser # temporarily attach root parser to each command parser # in order to parse known args root_parser = getattr(parser, '_root_parser', parser) with SuppressUsageOutput({parser} | set(command_parsers.values())): args = argv # for completion use the arguments provided by the argcomplete env var if _is_completion_requested(): from argcomplete import split_line _, _, _, comp_words, _ = split_line(os.environ['COMP_LINE']) args = comp_words[1:] try: known_args, _ = root_parser.parse_known_args(args=args) except SystemExit: if not _is_completion_requested(): raise # if the partial arguments can't be parsed use no known args known_args = argparse.Namespace(**{subparser.dest: None}) # check if a specific subparser is selected name = getattr(known_args, subparser.dest) if name is None: # add description for all command extensions to the root parser command_extensions = get_command_extensions(group_name) if command_extensions: description = '' max_length = max( len(name) for name in command_extensions.keys() if hide_extensions is None or name not in hide_extensions) for name in sorted(command_extensions.keys()): if hide_extensions is not None and name in hide_extensions: continue extension = command_extensions[name] description += '%s %s\n' % (name.ljust(max_length), get_first_line_doc(extension)) command_parser = command_parsers[name] command_parser.set_defaults(**{dest: extension}) mutable_description.value = description else: # add description for the selected command extension to the subparser command_extensions = get_command_extensions( group_name, exclude_names=set(entry_points.keys() - {name})) extension = command_extensions[name] command_parser = command_parsers[name] command_parser.set_defaults(**{dest: extension}) command_parser.description = get_first_line_doc(extension) # add the arguments for the requested extension if hasattr(extension, 'add_arguments'): command_parser = command_parsers[name] command_parser._root_parser = root_parser signature = inspect.signature(extension.add_arguments) kwargs = {} if 'argv' in signature.parameters: kwargs['argv'] = argv extension.add_arguments(command_parser, f'{cli_name} {name}', **kwargs) del command_parser._root_parser return subparser