Merge branch 'improved-tools-messages'

* improved-tools-messages:
  tools: suppress full traceback in console tools
  tools: add SubParsersHelpAction, which include subcommands details in --help
This commit is contained in:
Marek Marczykowski-Górecki 2018-03-20 19:19:40 +01:00
commit a99acc68da
No known key found for this signature in database
GPG Key ID: 063938BA42CFA724
9 changed files with 82 additions and 10 deletions

View File

@ -339,7 +339,7 @@ class QubesArgumentParser(argparse.ArgumentParser):
def __init__(self, want_app=True, want_app_no_instance=False,
vmname_nargs=None, **kwargs):
super(QubesArgumentParser, self).__init__(**kwargs)
super(QubesArgumentParser, self).__init__(add_help=False, **kwargs)
self._want_app = want_app
self._want_app_no_instance = want_app_no_instance
@ -360,6 +360,9 @@ class QubesArgumentParser(argparse.ArgumentParser):
self.add_argument('--force-root', action='store_true',
default=False, help=argparse.SUPPRESS)
self.add_argument('--help', '-h', action=SubParsersHelpAction,
help='show this help message and exit')
if self._vmname_nargs in [argparse.ZERO_OR_MORE, argparse.ONE_OR_MORE]:
vm_name_group = VmNameGroup(self,
required=(self._vmname_nargs
@ -436,6 +439,37 @@ class QubesArgumentParser(argparse.ArgumentParser):
print(*args, file=sys.stderr, **kwargs)
class SubParsersHelpAction(argparse._HelpAction):
''' Print help for all options _and all subparsers_ '''
# source https://stackoverflow.com/a/24122778
# pylint: disable=protected-access,too-few-public-methods
@staticmethod
def _indent(indent, text):
'''Indent *text* by *indent* spaces'''
return '\n'.join((' ' * indent) + l for l in text.splitlines())
def __call__(self, parser, namespace, values, option_string=None):
parser.print_help()
# retrieve subparsers from parser
subparsers_actions = [
action for action in parser._actions
if isinstance(action, argparse._SubParsersAction)]
# there will probably only be one subparser_action,
# but better save than sorry
for subparsers_action in subparsers_actions:
# get all subparsers and print help
for pseudo_action in subparsers_action._choices_actions:
choice = pseudo_action.dest.split(' ', 1)[0]
subparser = subparsers_action.choices[choice]
print("\nCommand '{}':".format(choice))
choice_help = subparser.format_usage()
choice_help = self._indent(2, choice_help)
print(choice_help)
parser.exit()
class AliasedSubParsersAction(argparse._SubParsersAction):
'''SubParser with support for action aliases'''
# source https://gist.github.com/sampsyo/471779

View File

@ -157,9 +157,13 @@ def main(args=None, app=None):
else:
profile_name = args.profile
backup_summary = args.app.qubesd_call(
'dom0', 'admin.backup.Info', profile_name)
print(backup_summary.decode())
try:
backup_summary = args.app.qubesd_call(
'dom0', 'admin.backup.Info', profile_name)
print(backup_summary.decode())
except QubesException as err:
print('\nBackup preparation error: {}'.format(err), file=sys.stderr)
return 1
if not args.yes:
if input("Do you want to proceed? [y/N] ").upper() != "Y":

View File

@ -24,6 +24,7 @@
import sys
import qubesadmin.exc
from qubesadmin.tools import QubesArgumentParser
parser = QubesArgumentParser(description=__doc__, vmname_nargs=1)
@ -70,7 +71,10 @@ def main(args=None, app=None):
parser.error(
'Pool argument must be of form: -P volume_name=pool_name')
app.clone_vm(src_vm, new_name, new_cls=args.cls, pool=pool, pools=pools)
try:
app.clone_vm(src_vm, new_name, new_cls=args.cls, pool=pool, pools=pools)
except qubesadmin.exc.QubesException as e:
parser.error_runtime(e)
if __name__ == '__main__':
sys.exit(main())

View File

@ -63,8 +63,11 @@ def main(args=None, app=None):
if args.delete:
parser.error('--unset requires a feature')
features = [(feat, vm.features[feat]) for feat in vm.features]
qubesadmin.tools.print_table(features)
try:
features = [(feat, vm.features[feat]) for feat in vm.features]
qubesadmin.tools.print_table(features)
except qubesadmin.exc.QubesException as e:
parser.error_runtime(e)
elif args.delete:
if args.value is not None:
@ -73,6 +76,8 @@ def main(args=None, app=None):
del vm.features[args.feature]
except KeyError:
pass
except qubesadmin.exc.QubesException as e:
parser.error_runtime(e)
elif args.value is None:
try:
@ -80,8 +85,13 @@ def main(args=None, app=None):
return 0
except KeyError:
return 1
except qubesadmin.exc.QubesException as e:
parser.error_runtime(e)
else:
vm.features[args.feature] = args.value
try:
vm.features[args.feature] = args.value
except qubesadmin.exc.QubesException as e:
parser.error_runtime(e)
return 0

View File

@ -168,6 +168,8 @@ def main(args=None, app=None):
args.app.remove_pool(args.name)
except KeyError:
parser.print_error('no such pool %s\n' % args.name)
except qubesadmin.exc.QubesException as e:
parser.error('failed to remove pool %s: %s\n' % (args.name, str(e)))
elif args.command == 'info':
for pool in args.pools:
pool_info(pool)

View File

@ -116,6 +116,8 @@ def process_actions(parser, args, target):
setattr(target, args.property, args.value)
except AttributeError:
parser.error('no such property: {!r}'.format(args.property))
except qubesadmin.exc.QubesException as e:
parser.error_runtime(e)
return 0
if args.delete:
@ -123,6 +125,8 @@ def process_actions(parser, args, target):
delattr(target, args.property)
except AttributeError:
parser.error('no such property: {!r}'.format(args.property))
except qubesadmin.exc.QubesException as e:
parser.error_runtime(e)
return 0
try:
@ -131,6 +135,8 @@ def process_actions(parser, args, target):
print(str(value))
except AttributeError:
parser.error('no such property: {!r}'.format(args.property))
except qubesadmin.exc.QubesException as e:
parser.error_runtime(e)
return 0

View File

@ -23,6 +23,7 @@
import sys
import qubesadmin.exc
from qubesadmin.tools import QubesArgumentParser
parser = QubesArgumentParser(description=__doc__,
@ -44,7 +45,10 @@ def main(args=None, app=None): # pylint: disable=missing-docstring
if args.no_confirm or go_ahead == "Y":
for vm in args.domains:
del args.app.domains[vm.name]
try:
del args.app.domains[vm.name]
except qubesadmin.exc.QubesException as e:
parser.error_runtime(e)
retcode = 0
else:
print("Remove cancelled.")

View File

@ -92,6 +92,8 @@ def main(args=None, app=None): # pylint: disable=missing-docstring
except qubesadmin.exc.QubesVMNotStartedError:
# already shut down
pass
except qubesadmin.exc.QubesException as e:
parser.error_runtime(e)
else:
timeout = args.timeout
current_vms = list(sorted(this_round_domains))
@ -114,6 +116,8 @@ def main(args=None, app=None): # pylint: disable=missing-docstring
except qubesadmin.exc.QubesVMNotStartedError:
# already shut down
pass
except qubesadmin.exc.QubesException as e:
parser.error_runtime(e)
if args.wait:
if have_events:

View File

@ -26,6 +26,7 @@ from __future__ import print_function
import sys
import qubesadmin
import qubesadmin.exc
import qubesadmin.tools
def mode_query(args):
@ -101,7 +102,10 @@ def main(args=None, app=None):
parser = get_parser()
args = parser.parse_args(args, app=app)
return args.func(args)
try:
return args.func(args)
except qubesadmin.exc.QubesException as e:
parser.error_runtime(e)
if __name__ == '__main__':