Repository: VirtusLab/git-machete
Pull request #706 (2022-10-20 15:11:11)

Issue #705: Add `ExitCode` enum


Patch:
diff --git a/git_machete/cli.py b/git_machete/cli.py
index f604775..aec1bb3 100644
--- a/git_machete/cli.py
+++ b/git_machete/cli.py
@@ -12,7 +12,7 @@ from git_machete import __version__
 from git_machete import utils
 from git_machete.client import MacheteClient
 from git_machete.generated_docs import short_docs, long_docs
-from git_machete.exceptions import MacheteException, StopInteraction
+from git_machete.exceptions import ExitCode, MacheteException, StopInteraction
 from git_machete.git_operations import AnyBranchName, AnyRevision, GitContext, LocalBranchShortName, RemoteBranchShortName
 from git_machete.utils import bold, fmt, underline, excluding, warn
 
@@ -115,7 +115,7 @@ class MacheteHelpAction(argparse.Action):
         # parser name (prog) is expected to be `git machete` or `git machete <command>`
         command_name = parser.prog.replace('git machete', '').strip()
         print(get_help_description(command_name))
-        parser.exit(status=None)
+        parser.exit(status=ExitCode.SUCCESS)
 
 
 def create_cli_parser() -> argparse.ArgumentParser:
@@ -477,7 +477,7 @@ def get_local_branch_short_name_from_arg(
     return LocalBranchShortName.of(branch_from_arg.replace('refs/heads/', '')) if branch_from_arg else None
 
 
-def exit_script(status_code: Optional[int] = None, error: Optional[BaseException] = None) -> None:
+def exit_script(status_code: int, error: Optional[BaseException] = None) -> None:
     # Single point of exit is useful, because we can mock this method in tests
     # and verify that actual errors like MacheteException are raised with
     # appropriate messages. Otherwise it's not possible, because SystemError
@@ -505,16 +505,16 @@ def launch(orig_args: List[str]) -> None:
 
         if not orig_args:
             print(get_help_description())
-            exit_script(2)
+            exit_script(ExitCode.ARGUMENT_ERROR)
 
         cmd = parsed_cli.command
 
         if cmd == "help":
             print(get_help_description(parsed_cli.topic_or_cmd))
-            exit_script()
+            exit_script(ExitCode.SUCCESS)
         elif cmd == "version":
             version()
-            exit_script()
+            exit_script(ExitCode.SUCCESS)
 
         machete_client = MacheteClient(git)
 
@@ -670,7 +670,7 @@ def launch(orig_args: List[str]) -> None:
             machete_client.read_definition_file(perform_interactive_slide_out=should_perform_interactive_slide_out)
             branch = get_local_branch_short_name_from_arg_or_current_branch(cli_opts.opt_branch, git)
             if branch is None or branch not in machete_client.managed_branches:
-                exit_script(1)
+                exit_script(ExitCode.MACHETE_EXCEPTION)
         elif cmd == "list":
             category = parsed_cli.category
             if category == 'slidable-after' and 'branch' not in parsed_cli_as_dict:
@@ -817,13 +817,15 @@ def launch(orig_args: List[str]) -> None:
                 opt_no_interactive_rebase=cli_opts.opt_no_interactive_rebase,
                 opt_fork_point=cli_opts.opt_fork_point)
 
-    except (KeyboardInterrupt, EOFError):
-        exit_script(3)
+    except EOFError:
+        exit_script(ExitCode.END_OF_FILE_SIGNAL)
+    except KeyboardInterrupt:
+        exit_script(ExitCode.KEYBOARD_INTERRUPT)
     except (argparse.ArgumentError, argparse.ArgumentTypeError) as e:
         print(get_short_general_usage())
-        exit_script(2, e)
+        exit_script(ExitCode.ARGUMENT_ERROR, e)
     except MacheteException as e:
-        exit_script(1, e)
+        exit_script(ExitCode.MACHETE_EXCEPTION, e)
     except StopInteraction:
         pass
     finally:
diff --git a/git_machete/exceptions.py b/git_machete/exceptions.py
index b0f5d1c..0ea6a52 100644
--- a/git_machete/exceptions.py
+++ b/git_machete/exceptions.py
@@ -1,3 +1,5 @@
+from enum import IntEnum
+
 from git_machete import utils
 
 
@@ -26,3 +28,11 @@ class UnprocessableEntityHTTPError(MacheteException):
 
     def __str__(self) -> str:
         return str(self.msg)
+
+
+class ExitCode(IntEnum):
+    SUCCESS = 0
+    MACHETE_EXCEPTION = 1
+    ARGUMENT_ERROR = 2
+    KEYBOARD_INTERRUPT = 3
+    END_OF_FILE_SIGNAL = 4


Test patch:
diff --git a/tests/mockers.py b/tests/mockers.py
index ae0ab27..42436ec 100644
--- a/tests/mockers.py
+++ b/tests/mockers.py
@@ -406,12 +406,12 @@ def mock_should_perform_interactive_slide_out(cmd: str) -> bool:
     return True
 
 
-def mock_exit_script(status_code: Optional[int] = None, error: Optional[BaseException] = None) -> None:
+def mock_exit_script(status_code: int, error: Optional[BaseException] = None) -> None:
     if error:
         raise error
     else:
         sys.exit(status_code)
 
 
-def mock_exit_script_no_exit(status_code: Optional[int] = None, error: Optional[BaseException] = None) -> None:
+def mock_exit_script_no_exit(status_code: int, error: Optional[BaseException] = None) -> None:
     return
diff --git a/tests/test_help.py b/tests/test_help.py
index df4dcf9..af9de4d 100644
--- a/tests/test_help.py
+++ b/tests/test_help.py
@@ -2,6 +2,7 @@ from typing import Any, List
 
 import pytest
 
+from git_machete.exceptions import ExitCode
 from git_machete.cli import commands_and_aliases
 
 from unittest import mock
@@ -28,36 +29,34 @@ class TestHelp:
     def test_help(self, mocker: Any) -> None:
         mocker.patch('git_machete.utils.run_cmd', mock_run_cmd)  # to hide git outputs in tests
 
-        exit_when_success = None
-        exit_code_when_failed = 2
         help_topics: List[str] = ['config', 'format', 'hooks']
 
         with pytest.raises(SystemExit) as e:
             launch_command("help")
-        assert exit_when_success == e.value.code, \
+        assert ExitCode.SUCCESS == e.value.code, \
             "Verify that `git machete help` causes SystemExit with " \
-            f"{exit_when_success} exit code."
+            f"{ExitCode.SUCCESS} exit code."
 
         for command in commands_and_aliases:
 
             with pytest.raises(SystemExit) as e:
                 launch_command("help", command)
-            assert exit_when_success == e.value.code, \
+            assert ExitCode.SUCCESS == e.value.code, \
                 f"Verify that `git machete help {command}` causes SystemExit" \
-                f" with {exit_when_success} exit code."
+                f" with {ExitCode.SUCCESS} exit code."
 
             if command not in help_topics:
                 with pytest.raises(SystemExit) as e:
                     launch_command(command, "--help")
-                assert exit_when_success == e.value.code, \
+                assert ExitCode.SUCCESS == e.value.code, \
                     f"Verify that `git machete {command} --help` causes " \
-                    f"SystemExit with {exit_when_success} exit code."
+                    f"SystemExit with {ExitCode.SUCCESS} exit code."
             else:
                 with pytest.raises(SystemExit) as e:
                     launch_command(command, "--help")
-                assert exit_code_when_failed == e.value.code, \
+                assert ExitCode.ARGUMENT_ERROR == e.value.code, \
                     f"Verify that `git machete {command} --help` causes " \
-                    f"SystemExit with {exit_when_success} exit code."
+                    f"SystemExit with {ExitCode.ARGUMENT_ERROR} exit code."
 
     @mock.patch('git_machete.cli.exit_script', mock_exit_script_no_exit)
     def test_help_output_has_no_ansi_codes(self) -> None: