Revive the UserError/UsageError distinction
authorW. Trevor King <wking@drexel.edu>
Mon, 2 May 2011 21:48:18 +0000 (17:48 -0400)
committerW. Trevor King <wking@drexel.edu>
Mon, 2 May 2011 21:48:24 +0000 (17:48 -0400)
UsageError was removed back in

  commit bf3d434b244c57556bec979acbc658c30eb58221
  Author: W. Trevor King <wking@drexel.edu>
  Date:   Sat Dec 12 00:31:55 2009 -0500

      Added libbe.command.base (with Command class)...

because the distinction between UsageError and UserError was unclear.
I've brought it back to satisfy a request by Christian Heinrich:

On Sun, May 01, 2011 at 02:52:13AM +0200, Christian Heinrich wrote:
> 3.) Using wrong syntax should receive better help messages.
>
> Current:
>
> "be new" -> ERROR:
> Missing required argument SUMMARY
>
> Should be:
>
> "be new" -> usage: be new [options] SUMMARY
> ...

He suggested we print the full option list as well, but I've decided
to just print the usage summary and remind the user how to get the
full help message if they want it.

libbe/command/__init__.py
libbe/command/base.py
libbe/ui/command_line.py

index 1cad09600c2d6a0253b21f7216b459ddbdc27937..832218c1979b64901935a73d03470891e1db09e6 100644 (file)
@@ -19,6 +19,7 @@
 import base
 
 UserError = base.UserError
+UsageError = base.UsageError
 UnknownCommand = base.UnknownCommand
 get_command = base.get_command
 get_command_class = base.get_command_class
@@ -33,7 +34,7 @@ UnconnectedStorageGetter = base.UnconnectedStorageGetter
 StorageCallbacks = base.StorageCallbacks
 UserInterface = base.UserInterface
 
-__all__ = [UserError, UnknownCommand,
+__all__ = [UserError, UsageError, UnknownCommand,
            get_command, get_command_class, commands,
            Option, Argument, Command,
            InputOutput, StdInputOutput, StringInputOutput,
index b5f5a221aa27b581696ec44e2e7e6380484de3af..48bb7abb2c35733687e7899d8569874d2d260355 100644 (file)
@@ -27,13 +27,36 @@ import libbe.ui.util.user
 import libbe.util.encoding
 import libbe.util.plugin
 
-class UserError(Exception):
+
+class UserError (Exception):
+    "An error due to improper BE usage."
     pass
 
-class UnknownCommand(UserError):
-    def __init__(self, cmd):
-        Exception.__init__(self, "Unknown command '%s'" % cmd)
-        self.cmd = cmd
+
+class UsageError (UserError):
+    """A serious parsing error due to invalid BE command construction.
+
+    The distinction between `UserError`\s and the more specific
+    `UsageError`\s is that when displaying a `UsageError` to the user,
+    the user is pointed towards the command usage information.  Use
+    the more general `UserError` if you feel that usage information
+    would not be particularly enlightening.
+    """
+    def __init__(self, command=None, command_name=None, message=None):
+        super(UsageError, self).__init__(message)
+        self.command = command
+        if command_name is None and command is not None:
+            command_name = command.name
+        self.command_name = command_name
+        self.message = message
+
+
+class UnknownCommand (UsageError):
+    def __init__(self, command_name):
+        super(UnknownCommand, self).__init__(
+            command_name=command_name,
+            message="Unknown command '%s'" % command_name)
+
 
 def get_command(command_name):
     """Retrieves the module for a user command
index 52daa4bde2e52a6c1aab25bb5b2cf44ef8168587..3f70962dae11039e8dbb1c04f5c70c670690d45b 100644 (file)
@@ -137,8 +137,9 @@ class CmdOptionParser(optparse.OptionParser):
             raise libbe.command.UserError('Too many arguments')
         for arg in self.command.args[len(parsed_args):]:
             if arg.optional == False:
-                raise libbe.command.UserError(
-                    'Missing required argument %s' % arg.metavar)
+                raise libbe.command.UsageError(
+                    command=self.command,
+                    message='Missing required argument %s' % arg.metavar)
         return (options, parsed_args)
 
     def callback(self, option, opt, value, parser):
@@ -289,6 +290,13 @@ def dispatch(ui, command, args):
                 'See http://docs.python.org/library/locale.html for details',
                 ])
         return 1
+    except libbe.command.UsageError, e:
+        print >> ui.io.stdout, 'Usage Error:\n', e
+        if e.command:
+            print >> ui.io.stdout, e.command.usage()
+        print >> ui.io.stdout, 'For usage information, try'
+        print >> ui.io.stdout, '  be help %s' % e.command_name
+        return 1
     except libbe.command.UserError, e:
         print >> ui.io.stdout, 'ERROR:\n', e
         return 1
@@ -314,15 +322,19 @@ def main():
         options,args = parser.parse_args()
     except CallbackExit:
         return 0
-    except libbe.command.UserError, e:
-        if str(e).endswith('COMMAND'):
+    except libbe.command.UsageError, e:
+        if isinstance(e.command, BE):
             # no command given, print usage string
-            print >> ui.io.stdout, 'ERROR:'
-            print >> ui.io.stdout, be.usage(), '\n', e
+            print >> ui.io.stdout, 'Usage Error:\n', e
+            print >> ui.io.stdout, be.usage()
             print >> ui.io.stdout, 'For example, try'
             print >> ui.io.stdout, '  be help'
         else:
-            print >> ui.io.stdout, 'ERROR:\n', e
+            print >> ui.io.stdout, 'Usage Error:\n', e
+            if e.command:
+                print >> ui.io.stdout, e.command.usage()
+            print >> ui.io.stdout, 'For usage information, try'
+            print >> ui.io.stdout, '  be help %s' % e.command_name
         return 1
 
     command_name = args.pop(0)