Bug 1387307 - Add FailedCommandError to mach draft
authorJonas Finnemann Jensen <jopsen@gmail.com>
Thu, 03 Aug 2017 19:25:10 -0700
changeset 620880 99c9b5dd6453fb744f572c444e73da9c60eef201
parent 612696 7d2e89fb92331d7e4296391213c1e63db628e046
child 640836 868deb370f5507cfde7d5d62ad40a8ba83649af8
push id72185
push userbmo:jopsen@gmail.com
push dateFri, 04 Aug 2017 02:28:46 +0000
bugs1387307
milestone56.0a1
Bug 1387307 - Add FailedCommandError to mach MozReview-Commit-ID: 8brtYHruEi4
python/mach/mach/base.py
python/mach/mach/main.py
--- a/python/mach/mach/base.py
+++ b/python/mach/mach/base.py
@@ -2,24 +2,23 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from __future__ import absolute_import, unicode_literals
 
 
 class CommandContext(object):
     """Holds run-time state so it can easily be passed to command providers."""
-    def __init__(self, cwd=None, settings=None, log_manager=None,
-        commands=None, **kwargs):
+    def __init__(self, cwd=None, settings=None, log_manager=None, commands=None, **kwargs):
         self.cwd = cwd
         self.settings = settings
         self.log_manager = log_manager
         self.commands = commands
 
-        for k,v in kwargs.items():
+        for k, v in kwargs.items():
             setattr(self, k, v)
 
 
 class MachError(Exception):
     """Base class for all errors raised by mach itself."""
 
 
 class NoCommandError(MachError):
@@ -31,16 +30,32 @@ class UnknownCommandError(MachError):
 
     def __init__(self, command, verb, suggested_commands=None):
         MachError.__init__(self)
 
         self.command = command
         self.verb = verb
         self.suggested_commands = suggested_commands or []
 
+
 class UnrecognizedArgumentError(MachError):
     """Raised when an unknown argument is passed to mach."""
 
     def __init__(self, command, arguments):
         MachError.__init__(self)
 
         self.command = command
         self.arguments = arguments
+
+
+class FailedCommandError(Exception):
+    """Raised by commands to signal a handled failure to be printed by mach
+
+    When caught by mach a FailedCommandError will print message and exit
+    with ''exit_code''. The optional ''reason'' is a string in cases where
+    other scripts may wish to handle the exception, though this is generally
+    intended to communicate failure to mach.
+    """
+
+    def __init__(self, message, exit_code=1, reason=''):
+        Exception.__init__(self, message)
+        self.exit_code = exit_code
+        self.reason = reason
--- a/python/mach/mach/main.py
+++ b/python/mach/mach/main.py
@@ -18,16 +18,17 @@ import traceback
 import uuid
 
 from .base import (
     CommandContext,
     MachError,
     NoCommandError,
     UnknownCommandError,
     UnrecognizedArgumentError,
+    FailedCommandError,
 )
 
 from .decorators import (
     CommandArgument,
     CommandProvider,
     Command,
 )
 
@@ -447,16 +448,19 @@ To see more help for a specific command,
 
         handler = getattr(args, 'mach_handler')
 
         try:
             return Registrar._run_command_handler(handler, context=context,
                 debug_command=args.debug_command, **vars(args.command_args))
         except KeyboardInterrupt as ki:
             raise ki
+        except FailedCommandError as e:
+            print(e.message, file=sys.stderr)
+            return e.exit_code
         except Exception as e:
             exc_type, exc_value, exc_tb = sys.exc_info()
 
             # The first two frames are us and are never used.
             stack = traceback.extract_tb(exc_tb)[2:]
 
             # If we have nothing on the stack, the exception was raised as part
             # of calling the @Command method itself. This likely means a