2015-12-22 04:02:54 +00:00
|
|
|
import sys
|
|
|
|
import argparse
|
devtool: categorise and order subcommands in help output
The listing of subcommands in the --help output for devtool was starting
to get difficult to follow, with commands appearing in no particular
order (due to some being in separate modules and the order of those
modules being parsed). Logically grouping the subcommands as well as
being able to exercise some control over the order of the subcommands
and groups would help, if we do so without losing the dynamic nature of
the list (i.e. that it comes from the plugins). Argparse provides no
built-in way to handle this and really, really makes it a pain to add,
but with some subclassing and hacking it's now possible, and can be
extended by any plugin as desired.
To put a subcommand into a group, all you need to do is specify a group=
parameter in the call to subparsers.add_parser(). you can also specify
an order= parameter to make the subcommand sort higher or lower in the
list (higher order numbers appear first, so use negative numbers to
force items to the end if that's what you want). To add a new group, use
subparsers.add_subparser_group(), supplying the name, description and
optionally an order number for the group itself (again, higher numbers
appear first).
(From OE-Core rev: e1b9d31e6ea3c254ecfe940fe795af44761e0e69)
Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2016-02-19 09:38:53 +00:00
|
|
|
from collections import defaultdict, OrderedDict
|
2015-12-22 04:02:54 +00:00
|
|
|
|
2015-12-22 04:03:11 +00:00
|
|
|
class ArgumentUsageError(Exception):
|
|
|
|
"""Exception class you can raise (and catch) in order to show the help"""
|
|
|
|
def __init__(self, message, subcommand=None):
|
|
|
|
self.message = message
|
|
|
|
self.subcommand = subcommand
|
|
|
|
|
2015-12-22 04:02:54 +00:00
|
|
|
class ArgumentParser(argparse.ArgumentParser):
|
|
|
|
"""Our own version of argparse's ArgumentParser"""
|
devtool: categorise and order subcommands in help output
The listing of subcommands in the --help output for devtool was starting
to get difficult to follow, with commands appearing in no particular
order (due to some being in separate modules and the order of those
modules being parsed). Logically grouping the subcommands as well as
being able to exercise some control over the order of the subcommands
and groups would help, if we do so without losing the dynamic nature of
the list (i.e. that it comes from the plugins). Argparse provides no
built-in way to handle this and really, really makes it a pain to add,
but with some subclassing and hacking it's now possible, and can be
extended by any plugin as desired.
To put a subcommand into a group, all you need to do is specify a group=
parameter in the call to subparsers.add_parser(). you can also specify
an order= parameter to make the subcommand sort higher or lower in the
list (higher order numbers appear first, so use negative numbers to
force items to the end if that's what you want). To add a new group, use
subparsers.add_subparser_group(), supplying the name, description and
optionally an order number for the group itself (again, higher numbers
appear first).
(From OE-Core rev: e1b9d31e6ea3c254ecfe940fe795af44761e0e69)
Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2016-02-19 09:38:53 +00:00
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
kwargs.setdefault('formatter_class', OeHelpFormatter)
|
|
|
|
self._subparser_groups = OrderedDict()
|
|
|
|
super(ArgumentParser, self).__init__(*args, **kwargs)
|
2016-04-27 23:24:01 +00:00
|
|
|
self._positionals.title = 'arguments'
|
2016-04-27 23:24:00 +00:00
|
|
|
self._optionals.title = 'options'
|
2015-12-22 04:02:54 +00:00
|
|
|
|
|
|
|
def error(self, message):
|
2016-04-27 23:23:58 +00:00
|
|
|
"""error(message: string)
|
|
|
|
|
|
|
|
Prints a help message incorporating the message to stderr and
|
|
|
|
exits.
|
|
|
|
"""
|
|
|
|
self._print_message('%s: error: %s\n' % (self.prog, message), sys.stderr)
|
|
|
|
self.print_help(sys.stderr)
|
2015-12-22 04:02:54 +00:00
|
|
|
sys.exit(2)
|
|
|
|
|
2015-12-22 04:03:11 +00:00
|
|
|
def error_subcommand(self, message, subcommand):
|
|
|
|
if subcommand:
|
2016-04-27 23:23:59 +00:00
|
|
|
action = self._get_subparser_action()
|
|
|
|
try:
|
|
|
|
subparser = action._name_parser_map[subcommand]
|
|
|
|
except KeyError:
|
|
|
|
self.error('no subparser for name "%s"' % subcommand)
|
|
|
|
else:
|
|
|
|
subparser.error(message)
|
|
|
|
|
2015-12-22 04:03:11 +00:00
|
|
|
self.error(message)
|
|
|
|
|
2015-12-22 04:03:08 +00:00
|
|
|
def add_subparsers(self, *args, **kwargs):
|
2016-04-27 23:23:59 +00:00
|
|
|
if 'dest' not in kwargs:
|
|
|
|
kwargs['dest'] = '_subparser_name'
|
|
|
|
|
2015-12-22 04:03:08 +00:00
|
|
|
ret = super(ArgumentParser, self).add_subparsers(*args, **kwargs)
|
devtool: categorise and order subcommands in help output
The listing of subcommands in the --help output for devtool was starting
to get difficult to follow, with commands appearing in no particular
order (due to some being in separate modules and the order of those
modules being parsed). Logically grouping the subcommands as well as
being able to exercise some control over the order of the subcommands
and groups would help, if we do so without losing the dynamic nature of
the list (i.e. that it comes from the plugins). Argparse provides no
built-in way to handle this and really, really makes it a pain to add,
but with some subclassing and hacking it's now possible, and can be
extended by any plugin as desired.
To put a subcommand into a group, all you need to do is specify a group=
parameter in the call to subparsers.add_parser(). you can also specify
an order= parameter to make the subcommand sort higher or lower in the
list (higher order numbers appear first, so use negative numbers to
force items to the end if that's what you want). To add a new group, use
subparsers.add_subparser_group(), supplying the name, description and
optionally an order number for the group itself (again, higher numbers
appear first).
(From OE-Core rev: e1b9d31e6ea3c254ecfe940fe795af44761e0e69)
Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2016-02-19 09:38:53 +00:00
|
|
|
# Need a way of accessing the parent parser
|
|
|
|
ret._parent_parser = self
|
|
|
|
# Ensure our class gets instantiated
|
2015-12-22 04:03:08 +00:00
|
|
|
ret._parser_class = ArgumentSubParser
|
devtool: categorise and order subcommands in help output
The listing of subcommands in the --help output for devtool was starting
to get difficult to follow, with commands appearing in no particular
order (due to some being in separate modules and the order of those
modules being parsed). Logically grouping the subcommands as well as
being able to exercise some control over the order of the subcommands
and groups would help, if we do so without losing the dynamic nature of
the list (i.e. that it comes from the plugins). Argparse provides no
built-in way to handle this and really, really makes it a pain to add,
but with some subclassing and hacking it's now possible, and can be
extended by any plugin as desired.
To put a subcommand into a group, all you need to do is specify a group=
parameter in the call to subparsers.add_parser(). you can also specify
an order= parameter to make the subcommand sort higher or lower in the
list (higher order numbers appear first, so use negative numbers to
force items to the end if that's what you want). To add a new group, use
subparsers.add_subparser_group(), supplying the name, description and
optionally an order number for the group itself (again, higher numbers
appear first).
(From OE-Core rev: e1b9d31e6ea3c254ecfe940fe795af44761e0e69)
Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2016-02-19 09:38:53 +00:00
|
|
|
# Hacky way of adding a method to the subparsers object
|
|
|
|
ret.add_subparser_group = self.add_subparser_group
|
2015-12-22 04:03:08 +00:00
|
|
|
return ret
|
|
|
|
|
devtool: categorise and order subcommands in help output
The listing of subcommands in the --help output for devtool was starting
to get difficult to follow, with commands appearing in no particular
order (due to some being in separate modules and the order of those
modules being parsed). Logically grouping the subcommands as well as
being able to exercise some control over the order of the subcommands
and groups would help, if we do so without losing the dynamic nature of
the list (i.e. that it comes from the plugins). Argparse provides no
built-in way to handle this and really, really makes it a pain to add,
but with some subclassing and hacking it's now possible, and can be
extended by any plugin as desired.
To put a subcommand into a group, all you need to do is specify a group=
parameter in the call to subparsers.add_parser(). you can also specify
an order= parameter to make the subcommand sort higher or lower in the
list (higher order numbers appear first, so use negative numbers to
force items to the end if that's what you want). To add a new group, use
subparsers.add_subparser_group(), supplying the name, description and
optionally an order number for the group itself (again, higher numbers
appear first).
(From OE-Core rev: e1b9d31e6ea3c254ecfe940fe795af44761e0e69)
Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2016-02-19 09:38:53 +00:00
|
|
|
def add_subparser_group(self, groupname, groupdesc, order=0):
|
|
|
|
self._subparser_groups[groupname] = (groupdesc, order)
|
|
|
|
|
2016-04-27 23:23:59 +00:00
|
|
|
def parse_args(self, args=None, namespace=None):
|
|
|
|
"""Parse arguments, using the correct subparser to show the error."""
|
|
|
|
args, argv = self.parse_known_args(args, namespace)
|
|
|
|
if argv:
|
|
|
|
message = 'unrecognized arguments: %s' % ' '.join(argv)
|
|
|
|
if self._subparsers:
|
|
|
|
subparser = self._get_subparser(args)
|
|
|
|
subparser.error(message)
|
|
|
|
else:
|
|
|
|
self.error(message)
|
|
|
|
sys.exit(2)
|
|
|
|
return args
|
|
|
|
|
|
|
|
def _get_subparser(self, args):
|
|
|
|
action = self._get_subparser_action()
|
|
|
|
if action.dest == argparse.SUPPRESS:
|
|
|
|
self.error('cannot get subparser, the subparser action dest is suppressed')
|
|
|
|
|
|
|
|
name = getattr(args, action.dest)
|
|
|
|
try:
|
|
|
|
return action._name_parser_map[name]
|
|
|
|
except KeyError:
|
|
|
|
self.error('no subparser for name "%s"' % name)
|
|
|
|
|
|
|
|
def _get_subparser_action(self):
|
|
|
|
if not self._subparsers:
|
|
|
|
self.error('cannot return the subparser action, no subparsers added')
|
|
|
|
|
|
|
|
for action in self._subparsers._group_actions:
|
|
|
|
if isinstance(action, argparse._SubParsersAction):
|
|
|
|
return action
|
|
|
|
|
devtool: categorise and order subcommands in help output
The listing of subcommands in the --help output for devtool was starting
to get difficult to follow, with commands appearing in no particular
order (due to some being in separate modules and the order of those
modules being parsed). Logically grouping the subcommands as well as
being able to exercise some control over the order of the subcommands
and groups would help, if we do so without losing the dynamic nature of
the list (i.e. that it comes from the plugins). Argparse provides no
built-in way to handle this and really, really makes it a pain to add,
but with some subclassing and hacking it's now possible, and can be
extended by any plugin as desired.
To put a subcommand into a group, all you need to do is specify a group=
parameter in the call to subparsers.add_parser(). you can also specify
an order= parameter to make the subcommand sort higher or lower in the
list (higher order numbers appear first, so use negative numbers to
force items to the end if that's what you want). To add a new group, use
subparsers.add_subparser_group(), supplying the name, description and
optionally an order number for the group itself (again, higher numbers
appear first).
(From OE-Core rev: e1b9d31e6ea3c254ecfe940fe795af44761e0e69)
Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2016-02-19 09:38:53 +00:00
|
|
|
|
2015-12-22 04:03:08 +00:00
|
|
|
class ArgumentSubParser(ArgumentParser):
|
devtool: categorise and order subcommands in help output
The listing of subcommands in the --help output for devtool was starting
to get difficult to follow, with commands appearing in no particular
order (due to some being in separate modules and the order of those
modules being parsed). Logically grouping the subcommands as well as
being able to exercise some control over the order of the subcommands
and groups would help, if we do so without losing the dynamic nature of
the list (i.e. that it comes from the plugins). Argparse provides no
built-in way to handle this and really, really makes it a pain to add,
but with some subclassing and hacking it's now possible, and can be
extended by any plugin as desired.
To put a subcommand into a group, all you need to do is specify a group=
parameter in the call to subparsers.add_parser(). you can also specify
an order= parameter to make the subcommand sort higher or lower in the
list (higher order numbers appear first, so use negative numbers to
force items to the end if that's what you want). To add a new group, use
subparsers.add_subparser_group(), supplying the name, description and
optionally an order number for the group itself (again, higher numbers
appear first).
(From OE-Core rev: e1b9d31e6ea3c254ecfe940fe795af44761e0e69)
Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2016-02-19 09:38:53 +00:00
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
if 'group' in kwargs:
|
|
|
|
self._group = kwargs.pop('group')
|
|
|
|
if 'order' in kwargs:
|
|
|
|
self._order = kwargs.pop('order')
|
|
|
|
super(ArgumentSubParser, self).__init__(*args, **kwargs)
|
|
|
|
|
2015-12-22 04:03:08 +00:00
|
|
|
def parse_known_args(self, args=None, namespace=None):
|
|
|
|
# This works around argparse not handling optional positional arguments being
|
|
|
|
# intermixed with other options. A pretty horrible hack, but we're not left
|
|
|
|
# with much choice given that the bug in argparse exists and it's difficult
|
|
|
|
# to subclass.
|
|
|
|
# Borrowed from http://stackoverflow.com/questions/20165843/argparse-how-to-handle-variable-number-of-arguments-nargs
|
|
|
|
# with an extra workaround (in format_help() below) for the positional
|
|
|
|
# arguments disappearing from the --help output, as well as structural tweaks.
|
|
|
|
# Originally simplified from http://bugs.python.org/file30204/test_intermixed.py
|
|
|
|
positionals = self._get_positional_actions()
|
|
|
|
for action in positionals:
|
|
|
|
# deactivate positionals
|
|
|
|
action.save_nargs = action.nargs
|
|
|
|
action.nargs = 0
|
|
|
|
|
|
|
|
namespace, remaining_args = super(ArgumentSubParser, self).parse_known_args(args, namespace)
|
|
|
|
for action in positionals:
|
|
|
|
# remove the empty positional values from namespace
|
|
|
|
if hasattr(namespace, action.dest):
|
|
|
|
delattr(namespace, action.dest)
|
|
|
|
for action in positionals:
|
|
|
|
action.nargs = action.save_nargs
|
|
|
|
# parse positionals
|
|
|
|
namespace, extras = super(ArgumentSubParser, self).parse_known_args(remaining_args, namespace)
|
|
|
|
return namespace, extras
|
|
|
|
|
|
|
|
def format_help(self):
|
|
|
|
# Quick, restore the positionals!
|
|
|
|
positionals = self._get_positional_actions()
|
|
|
|
for action in positionals:
|
|
|
|
if hasattr(action, 'save_nargs'):
|
|
|
|
action.nargs = action.save_nargs
|
|
|
|
return super(ArgumentParser, self).format_help()
|
devtool: categorise and order subcommands in help output
The listing of subcommands in the --help output for devtool was starting
to get difficult to follow, with commands appearing in no particular
order (due to some being in separate modules and the order of those
modules being parsed). Logically grouping the subcommands as well as
being able to exercise some control over the order of the subcommands
and groups would help, if we do so without losing the dynamic nature of
the list (i.e. that it comes from the plugins). Argparse provides no
built-in way to handle this and really, really makes it a pain to add,
but with some subclassing and hacking it's now possible, and can be
extended by any plugin as desired.
To put a subcommand into a group, all you need to do is specify a group=
parameter in the call to subparsers.add_parser(). you can also specify
an order= parameter to make the subcommand sort higher or lower in the
list (higher order numbers appear first, so use negative numbers to
force items to the end if that's what you want). To add a new group, use
subparsers.add_subparser_group(), supplying the name, description and
optionally an order number for the group itself (again, higher numbers
appear first).
(From OE-Core rev: e1b9d31e6ea3c254ecfe940fe795af44761e0e69)
Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
2016-02-19 09:38:53 +00:00
|
|
|
|
|
|
|
|
|
|
|
class OeHelpFormatter(argparse.HelpFormatter):
|
|
|
|
def _format_action(self, action):
|
|
|
|
if hasattr(action, '_get_subactions'):
|
|
|
|
# subcommands list
|
|
|
|
groupmap = defaultdict(list)
|
|
|
|
ordermap = {}
|
|
|
|
subparser_groups = action._parent_parser._subparser_groups
|
|
|
|
groups = sorted(subparser_groups.keys(), key=lambda item: subparser_groups[item][1], reverse=True)
|
|
|
|
for subaction in self._iter_indented_subactions(action):
|
|
|
|
parser = action._name_parser_map[subaction.dest]
|
|
|
|
group = getattr(parser, '_group', None)
|
|
|
|
groupmap[group].append(subaction)
|
|
|
|
if group not in groups:
|
|
|
|
groups.append(group)
|
|
|
|
order = getattr(parser, '_order', 0)
|
|
|
|
ordermap[subaction.dest] = order
|
|
|
|
|
|
|
|
lines = []
|
|
|
|
if len(groupmap) > 1:
|
|
|
|
groupindent = ' '
|
|
|
|
else:
|
|
|
|
groupindent = ''
|
|
|
|
for group in groups:
|
|
|
|
subactions = groupmap[group]
|
|
|
|
if not subactions:
|
|
|
|
continue
|
|
|
|
if groupindent:
|
|
|
|
if not group:
|
|
|
|
group = 'other'
|
|
|
|
groupdesc = subparser_groups.get(group, (group, 0))[0]
|
|
|
|
lines.append(' %s:' % groupdesc)
|
|
|
|
for subaction in sorted(subactions, key=lambda item: ordermap[item.dest], reverse=True):
|
|
|
|
lines.append('%s%s' % (groupindent, self._format_action(subaction).rstrip()))
|
|
|
|
return '\n'.join(lines)
|
|
|
|
else:
|
|
|
|
return super(OeHelpFormatter, self)._format_action(action)
|