commands: Add usage to CommandError
This commit is contained in:
parent
001c8e0bdc
commit
b5360a1360
@ -5,7 +5,9 @@ import sys
|
|||||||
|
|
||||||
|
|
||||||
class CommandError(Exception):
|
class CommandError(Exception):
|
||||||
pass
|
def __init__(self, message, usage=None):
|
||||||
|
self.message = message
|
||||||
|
self.usage = usage
|
||||||
|
|
||||||
|
|
||||||
class ArgumentParser(argparse.ArgumentParser):
|
class ArgumentParser(argparse.ArgumentParser):
|
||||||
@ -42,9 +44,12 @@ class Command(object):
|
|||||||
def format_usage(self, prog=None):
|
def format_usage(self, prog=None):
|
||||||
actions = self._build()[1]
|
actions = self._build()[1]
|
||||||
prog = prog or os.path.basename(sys.argv[0])
|
prog = prog or os.path.basename(sys.argv[0])
|
||||||
|
return self._usage(actions, prog) + '\n'
|
||||||
|
|
||||||
|
def _usage(self, actions, prog):
|
||||||
formatter = argparse.HelpFormatter(prog)
|
formatter = argparse.HelpFormatter(prog)
|
||||||
formatter.add_usage(None, actions, [])
|
formatter.add_usage(None, actions, [])
|
||||||
return formatter.format_help()
|
return formatter.format_help().strip()
|
||||||
|
|
||||||
def format_help(self, prog=None):
|
def format_help(self, prog=None):
|
||||||
actions = self._build()[1]
|
actions = self._build()[1]
|
||||||
@ -89,16 +94,20 @@ class Command(object):
|
|||||||
for childname, child in self._children.items():
|
for childname, child in self._children.items():
|
||||||
child._subhelp(' '.join((name, childname)), result)
|
child._subhelp(' '.join((name, childname)), result)
|
||||||
|
|
||||||
def parse(self, args):
|
def parse(self, args, prog=None):
|
||||||
return self._parse(args, argparse.Namespace(), self._defaults.copy())
|
prog = prog or os.path.basename(sys.argv[0])
|
||||||
|
return self._parse(
|
||||||
|
args, argparse.Namespace(), self._defaults.copy(), prog)
|
||||||
|
|
||||||
def _parse(self, args, namespace, defaults):
|
def _parse(self, args, namespace, defaults, prog):
|
||||||
defaults.update(self._defaults)
|
defaults.update(self._defaults)
|
||||||
parser = self._build()[0]
|
parser, actions = self._build()
|
||||||
result, unknown = parser.parse_known_args(args, namespace)
|
|
||||||
|
|
||||||
if unknown:
|
try:
|
||||||
raise CommandError('Unknown command options.')
|
result = parser.parse_args(args, namespace)
|
||||||
|
except CommandError as e:
|
||||||
|
e.usage = self._usage(actions, prog)
|
||||||
|
raise
|
||||||
|
|
||||||
if not result._args:
|
if not result._args:
|
||||||
for attr, value in defaults.items():
|
for attr, value in defaults.items():
|
||||||
@ -108,8 +117,10 @@ class Command(object):
|
|||||||
result.command = self
|
result.command = self
|
||||||
return result
|
return result
|
||||||
|
|
||||||
child = self._children.get(result._args[0])
|
child = result._args.pop(0)
|
||||||
if not child:
|
if child not in self._children:
|
||||||
raise CommandError('Invalid sub-command provided.')
|
raise CommandError('unrecognized command: %s' % child,
|
||||||
|
usage=self._usage(actions, prog))
|
||||||
|
|
||||||
return child._parse(result._args[1:], result, defaults)
|
return self._children[child]._parse(
|
||||||
|
result._args, result, defaults, ' '.join([prog, child]))
|
||||||
|
|||||||
@ -95,28 +95,46 @@ class CommandParsingTest(unittest.TestCase):
|
|||||||
cmd.add_argument('--bar', type=int)
|
cmd.add_argument('--bar', type=int)
|
||||||
|
|
||||||
with self.assertRaises(command.CommandError) as cm:
|
with self.assertRaises(command.CommandError) as cm:
|
||||||
cmd.parse(['--bar', b'zero'])
|
cmd.parse(['--bar', b'zero'], prog='foo')
|
||||||
|
|
||||||
self.assertEqual(cm.exception.message,
|
self.assertEqual(cm.exception.message,
|
||||||
"argument --bar: invalid int value: 'zero'")
|
"argument --bar: invalid int value: 'zero'")
|
||||||
|
self.assertEqual(cm.exception.usage, 'usage: foo [--bar BAR]')
|
||||||
|
|
||||||
|
@mock.patch('sys.argv')
|
||||||
|
def test_command_error_usage_prog(self, argv_mock):
|
||||||
|
argv_mock.__getitem__.return_value = '/usr/bin/foo'
|
||||||
|
|
||||||
|
cmd = command.Command()
|
||||||
|
cmd.add_argument('--bar', required=True)
|
||||||
|
|
||||||
|
with self.assertRaises(command.CommandError) as cm:
|
||||||
|
cmd.parse([])
|
||||||
|
self.assertEqual(cm.exception.usage, 'usage: foo --bar BAR')
|
||||||
|
|
||||||
|
with self.assertRaises(command.CommandError) as cm:
|
||||||
|
cmd.parse([], prog='baz')
|
||||||
|
self.assertEqual(cm.exception.usage, 'usage: baz --bar BAR')
|
||||||
|
|
||||||
def test_missing_required(self):
|
def test_missing_required(self):
|
||||||
cmd = command.Command()
|
cmd = command.Command()
|
||||||
cmd.add_argument('--bar', required=True)
|
cmd.add_argument('--bar', required=True)
|
||||||
|
|
||||||
with self.assertRaises(command.CommandError) as cm:
|
with self.assertRaises(command.CommandError) as cm:
|
||||||
cmd.parse([])
|
cmd.parse([], prog='foo')
|
||||||
|
|
||||||
self.assertEqual(cm.exception.message, 'argument --bar is required')
|
self.assertEqual(cm.exception.message, 'argument --bar is required')
|
||||||
|
self.assertEqual(cm.exception.usage, 'usage: foo --bar BAR')
|
||||||
|
|
||||||
def test_missing_positionals(self):
|
def test_missing_positionals(self):
|
||||||
cmd = command.Command()
|
cmd = command.Command()
|
||||||
cmd.add_argument('bar')
|
cmd.add_argument('bar')
|
||||||
|
|
||||||
with self.assertRaises(command.CommandError) as cm:
|
with self.assertRaises(command.CommandError) as cm:
|
||||||
cmd.parse([])
|
cmd.parse([], prog='foo')
|
||||||
|
|
||||||
self.assertEqual(cm.exception.message, 'too few arguments')
|
self.assertEqual(cm.exception.message, 'too few arguments')
|
||||||
|
self.assertEqual(cm.exception.usage, 'usage: foo bar')
|
||||||
|
|
||||||
def test_missing_positionals_subcommand(self):
|
def test_missing_positionals_subcommand(self):
|
||||||
child = command.Command()
|
child = command.Command()
|
||||||
@ -126,9 +144,30 @@ class CommandParsingTest(unittest.TestCase):
|
|||||||
cmd.add_child('bar', child)
|
cmd.add_child('bar', child)
|
||||||
|
|
||||||
with self.assertRaises(command.CommandError) as cm:
|
with self.assertRaises(command.CommandError) as cm:
|
||||||
cmd.parse(['bar'])
|
cmd.parse(['bar'], prog='foo')
|
||||||
|
|
||||||
self.assertEqual(cm.exception.message, 'too few arguments')
|
self.assertEqual(cm.exception.message, 'too few arguments')
|
||||||
|
self.assertEqual(cm.exception.usage, 'usage: foo bar baz')
|
||||||
|
|
||||||
|
def test_unknown_command(self):
|
||||||
|
cmd = command.Command()
|
||||||
|
|
||||||
|
with self.assertRaises(command.CommandError) as cm:
|
||||||
|
cmd.parse(['--help'], prog='foo')
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
cm.exception.message, 'unrecognized arguments: --help')
|
||||||
|
self.assertEqual(cm.exception.usage, 'usage: foo')
|
||||||
|
|
||||||
|
def test_invalid_subcommand(self):
|
||||||
|
cmd = command.Command()
|
||||||
|
cmd.add_child('baz', command.Command())
|
||||||
|
|
||||||
|
with self.assertRaises(command.CommandError) as cm:
|
||||||
|
cmd.parse(['bar'], prog='foo')
|
||||||
|
|
||||||
|
self.assertEqual(cm.exception.message, 'unrecognized command: bar')
|
||||||
|
self.assertEqual(cm.exception.usage, 'usage: foo')
|
||||||
|
|
||||||
def test_set_defaults(self):
|
def test_set_defaults(self):
|
||||||
cmd = command.Command()
|
cmd = command.Command()
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user