1
0
forked from drath/libratbag

tools: ratbagctl: have a full description of the commands in the usage

I originally intended to have a dynamic use of argparse to generate
the commands, but it turned out to be a nightmare.

Just go down the tree of arguments and format the help properly.

Fixes #288

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
This commit is contained in:
Benjamin Tissoires
2017-08-22 18:41:12 +02:00
committed by Peter Hutterer
parent c1c8b1eda2
commit 330b4641ad

View File

@@ -531,6 +531,8 @@ def func_profile_disable(r, args):
of_type = 'type'
"""the name of the element, it'll be the one matching the args on the CLI"""
name = 'name'
"""the group to logically associate commands while printing the help"""
group = 'group'
"""list of positional arguments for the given command"""
pos_args = 'pos_args'
"""a tag that we can refer latrer in an element of type 'link'"""
@@ -625,20 +627,23 @@ parser_def = [
{
of_type: command,
name: 'list',
help_str: 'List available configurable devices',
help_str: 'List supported devices (does not take a device argument)',
func: list_devices,
group: 'General',
},
{
of_type: command,
name: 'info',
help_str: 'Show device information',
func: show_device,
group: 'Device',
},
{
of_type: switch,
name: 'profile',
help_str: 'Access profile information',
tag: 'profile',
group: 'Profile',
switch: [
{
of_type: switch,
@@ -678,7 +683,7 @@ parser_def = [
{
of_type: command,
name: 'get',
help_str: 'Show current active profile',
help_str: 'Show selected profile information',
func: func_profile_get,
},
{
@@ -719,8 +724,12 @@ parser_def = [
{
of_type: switch,
name: 'resolution',
help_str: 'Access resolution information',
help_str: """Access resolution information
Resolution commands work on the given profile, or on the
active profile if none is given.""",
tag: 'resolution',
group: 'Resolution',
switch: [
{
of_type: switch,
@@ -805,8 +814,12 @@ parser_def = [
{
of_type: switch,
name: 'dpi',
help_str: 'Access DPI information',
help_str: """Access DPI information
DPI commands work on the given profile and resolution, or on the
active resolution of the active profile if none are given.""",
tag: 'dpi',
group: 'DPI',
switch: [
{
of_type: command,
@@ -834,8 +847,12 @@ parser_def = [
{
of_type: switch,
name: 'rate',
help_str: 'Access report rate information',
help_str: """Access report rate information
Rate commands work on the given profile and resolution, or on the
active resolution of the active profile if none are given.""",
tag: 'rate',
group: 'Rate',
switch: [
{
of_type: command,
@@ -863,8 +880,12 @@ parser_def = [
{
of_type: switch,
name: 'button',
help_str: 'Access Button information',
help_str: """Access Button information
Button commands work on the given profile, or on the
active profile if none is given.""",
tag: 'button',
group: 'Button',
switch: [
{
of_type: command,
@@ -924,7 +945,7 @@ parser_def = [
{
of_type: argument,
name: 'target_special',
metavar: 'special',
metavar: 'S',
help_str: 'The new special value to assign',
choices: [
"unknown",
@@ -954,12 +975,20 @@ parser_def = [
{
of_type: command,
name: 'macro',
help_str: 'Set the button action to the given macro',
help_str: """Set the button action to the given macro
Macro syntax:
A macro is a series of key events or waiting periods.
Keys must be specified in linux/input.h key names.
KEY_A Press and release 'a'
+KEY_A Press 'a'
-KEY_A Release 'a'
t300 Wait 300ms""",
pos_args: [
{
of_type: argument,
name: 'target_macro',
metavar: 'macro',
metavar: '...',
help_str: 'The new macro to assign',
nargs: argparse.REMAINDER,
},
@@ -976,8 +1005,12 @@ parser_def = [
{
of_type: switch,
name: 'led',
help_str: 'Access LED information',
help_str: """Access LED information
LED commands work on the given profile, or on the
active profile if none is given.""",
tag: 'led',
group: 'LED',
switch: [
{
of_type: command,
@@ -1039,7 +1072,7 @@ parser_def = [
{
of_type: argument,
name: 'rate',
metavar: 'rate',
metavar: 'R',
help_str: 'The rate in Hz to set as current',
arg_type: int,
},
@@ -1053,7 +1086,7 @@ parser_def = [
{
of_type: argument,
name: 'brightness',
metavar: 'brightness',
metavar: 'B',
help_str: 'The brightness to set as current',
arg_type: u8,
},
@@ -1083,10 +1116,11 @@ class ParseError(Exception):
class RatbagParser(object):
tagged = {}
def __init__(self, type, name, tag=None, func=None, help=None):
def __init__(self, type, name, group=None, tag=None, func=None, help=None):
self.type = type
self.name = name
self.tag = tag
self.group = group
if tag is not None:
RatbagParser.tagged[tag] = self
self.func = func
@@ -1119,17 +1153,22 @@ class RatbagParser(object):
pr_debug(ns, "<.. {}: {} | {}".format(self.type, self.name, input_string))
return r
def print_help(self):
return str(type(self))
def build_cmd_args_name(self):
"""uniquely tag the arguments of the command in the namespace"""
return "{}_args_{}".format(self.name)
def print_help(self, group, prefix=""):
if self.group is not None:
print("\n{} Commands:".format(self.group))
self._print_help(prefix)
def _print_help(self, prefix):
raise ParseError("please implement _print_help on {}".format(type(self)))
class RatbagParserSwitch(RatbagParser):
def __init__(self, type, name, switch=[], N_access=None, tag=None, func=None, help=None):
super(RatbagParserSwitch, self).__init__(type, name, tag, func, help)
def __init__(self, type, name, group=None, switch=[], N_access=None, tag=None, func=None, help=None):
super(RatbagParserSwitch, self).__init__(type, name, group, tag, func, help)
self.switch = [classes[obj[of_type]](**obj) for obj in switch]
if N_access is not None:
self.N_access = RatbagParserNAccess(**N_access)
@@ -1154,9 +1193,11 @@ class RatbagParserSwitch(RatbagParser):
else:
# we have a single int as first argument, switch to the
# N_access subtree of the command
return self.N_access.sub_parse(input_string, ns)
return self.N_access._sub_parse(self, input_string, ns)
parser = argparse.ArgumentParser(add_help=False)
parser = argparse.ArgumentParser(prog="{} {}".format(sys.argv[0], self.name),
description=self.help,
add_help=False)
# create a new subparser to handle all commands
subs = parser.add_subparsers(title="COMMANDS", help=None)
@@ -1165,17 +1206,28 @@ class RatbagParserSwitch(RatbagParser):
return parser
def _print_help(self, prefix):
if self.help and self.group is not None:
string = self.help.split('\n')
string = "\n ".join(string)
print(" ", string, '\n')
for e in self.switch:
e.print_help(None, "{}{} ".format(prefix, self.name))
if self.N_access is not None:
self.N_access.print_help(None, self.name + " ")
def __repr__(self):
return "switch({})".format(self.repr_args())
class RatbagParserNAccess(RatbagParserSwitch):
def __init__(self, type, name, switch=[], metavar=None, tag=None, func=None, help=None):
super(RatbagParserNAccess, self).__init__(type, name, switch, None, tag, func, help)
def __init__(self, type, name, group=None, switch=[], metavar=None, tag=None, func=None, help=None):
super(RatbagParserNAccess, self).__init__(type, name, group, switch, None, tag, func, help)
self.metavar = metavar
def _sub_parse(self, input_string, ns):
parser = argparse.ArgumentParser(add_help=False)
def _sub_parse(self, parent, input_string, ns):
parser = argparse.ArgumentParser(prog="{} {}".format(sys.argv[0], parent.name),
add_help=False)
parser.add_argument(self.name, help=self.help, type=int)
# create a new subparser to handle all commands
subs = parser.add_subparsers(title="COMMANDS", help=None)
@@ -1189,17 +1241,22 @@ class RatbagParserNAccess(RatbagParserSwitch):
def __repr__(self):
return "N_Access({})".format(self.repr_args())
def _print_help(self, prefix):
for e in self.switch:
e.print_help(None, "{}N ".format(prefix))
class RatbagParserSet(RatbagParserSwitch):
def __init__(self, type, name, switch=[], N_access=None, tag=None, func=None, help=None):
super(RatbagParserSet, self).__init__(type, name, switch, N_access, tag, func, help)
def __init__(self, type, name, group=None, switch=[], N_access=None, tag=None, func=None, help=None):
super(RatbagParserSet, self).__init__(type, name, group, switch, N_access, tag, func, help)
def _add_to_subparsers(self, parent):
parser = parent.add_parser(self.name, help=self.help)
parser.set_defaults(subparse=self.sub_parse)
def _sub_parse(self, input_string, ns):
parser = argparse.ArgumentParser(add_help=False)
parser = argparse.ArgumentParser(prog="{} {}".format(sys.argv[0], self.name),
add_help=False)
# create a new subparser to handle all commands
subs = parser.add_subparsers(title="COMMANDS", help=None)
for e in self.switch:
@@ -1216,10 +1273,17 @@ class RatbagParserSet(RatbagParserSwitch):
def __repr__(self):
return "set({})".format(self.repr_args())
def _print_help(self, prefix):
command = prefix + "{COMMAND} ..."
print(" {:<36}{}".format(command,
self.help if self.help else ""))
for e in self.switch:
e.print_help(None, " " * len(prefix))
class RatbagParserCommand(RatbagParser):
def __init__(self, type, name, pos_args=[], tag=None, func=None, help=None):
super(RatbagParserCommand, self).__init__(type, name, tag, func, help)
def __init__(self, type, name, group=None, pos_args=[], tag=None, func=None, help=None):
super(RatbagParserCommand, self).__init__(type, name, group, tag, func, help)
self.pos_args = [classes[obj[of_type]](**obj) for obj in pos_args]
def _add_to_subparsers(self, parent):
@@ -1231,10 +1295,20 @@ class RatbagParserCommand(RatbagParser):
def __repr__(self):
return "command({})".format(self.repr_args())
def _print_help(self, prefix):
command = prefix + self.name
for a in self.pos_args:
if a.choices is None or len(a.choices) > 5:
command += " {}".format(a.metavar)
else:
command += " [{}]".format("|".join(a.choices))
print(" {:<36}{}".format(command,
self.help if self.help else ""))
class RatbagParserArgument(RatbagParser):
def __init__(self, type, name, arg_type=None, metavar=None, nargs=None, choices=None, tag=None, func=None, help=None):
super(RatbagParserArgument, self).__init__(type, name, tag, func, help)
def __init__(self, type, name, group=None, arg_type=None, metavar=None, nargs=None, choices=None, tag=None, func=None, help=None):
super(RatbagParserArgument, self).__init__(type, name, group, tag, func, help)
self.arg_type = arg_type
self.metavar = metavar
self.nargs = nargs
@@ -1245,17 +1319,24 @@ class RatbagParserArgument(RatbagParser):
class RatbagParserLink(RatbagParser):
def __init__(self, type, dest=None, tag=None, func=None, help=None):
super(RatbagParserLink, self).__init__(type, dest, tag, func, help)
def __init__(self, type, dest=None, group=None, tag=None, func=None, help=None):
super(RatbagParserLink, self).__init__(type, dest, group, tag, func, help)
self.dest = dest
def _add_to_subparsers(self, parent):
def get_dest(self):
try:
e = RatbagParser.tagged[self.dest]
dest = RatbagParser.tagged[self.dest]
except KeyError:
raise ParseError("link '{}' points to nothing".format(self.dest))
else:
e.add_to_subparsers(parent)
return dest
def _add_to_subparsers(self, parent):
dest = self.get_dest()
dest.add_to_subparsers(parent)
def _print_help(self, prefix):
dest = self.get_dest()
print(" {:<36}Use {}for '{} Commands'".format(prefix + self.dest + " ...", prefix, dest.group))
classes = {
switch: RatbagParserSwitch,
@@ -1278,7 +1359,7 @@ class RatbagParserRoot(object):
if not string.startswith('-'):
input_string[i] = string.lower()
self.parser = argparse.ArgumentParser(description="Inspect and modify configurable mice",
self.parser = argparse.ArgumentParser(description="Inspect and modify a configurable device",
add_help=False)
self.parser.add_argument("-V", "--version", action="version", version="@version@")
self.parser.add_argument('--verbose', '-v', action='count', default=0)
@@ -1289,7 +1370,10 @@ class RatbagParserRoot(object):
# retrieve the global options now and remove them from the processing
ns, rest = self.parser.parse_known_args(input_string)
subs = self.parser.add_subparsers(title="COMMANDS", help='sub-command help')
if ns.help:
return ns
subs = self.parser.add_subparsers(title="COMMANDS")
subparser = self.parser
@@ -1316,7 +1400,34 @@ class RatbagParserRoot(object):
return ns
def print_help(self):
self.parser.print_help()
print("usage: {} [OPTIONS] {{COMMAND}} ... /path/to/device\n".format(self.parser.prog))
print(self.parser.description)
print("""
Common options:
--versionm -V show program's version number and exit
--verbose, -v
--help, -h show this help and exit""")
if self.want_keepalive:
print(" --keepalive do not terminate ratbagd after the processing")
for c in self.children:
c.print_help(None)
print("""
Examples:
{0} profile active get
{0} profile 0 resolution active set 4
{0} profile 0 resolution 1 dpi get
{0} resolution 4 rate get
{0} dpi set 800
{0} profile 0 led 0 set mode on
{0} profile 0 led 0 set color ff00ff
{0} profile 0 led 0 set rate 20
Exit codes:
0 Success
1 Unsupported feature, index out of available range or invalid device
2 Commandline arguments are invalid
3 A command failed on the device
""".format(self.parser.prog))
def get_parser():