Skip to content

Commit 7ff3cee

Browse files
committed
Make _WinBashStatus instances carry all their info
This changes _WinBashStatus from an enum to a sum type so that, rather than having a global _WinBashStatus.ERROR_WHILE_CHECKING object whose error_or_process attribute may be absent and, if present, carries an object set on the most recent call to check(), we get a _WinBashStatus.ErrorWhileChecking instance that carries an error_or_process attribute specific to the call. Ordinarily this would not make a difference, other than that the global attribute usage was confusing in the code, because typically _WinBashStatus.check() is called only once. However, when debugging, having the old attribute on the same object be clobbered is undesirable. This adds the sumtypes library as a test dependency, to allow the enum to be rewritten as a sum type without loss of clarity. This is a development-only dependency; the main dependencies are unchanged.
1 parent 8621e89 commit 7ff3cee

File tree

2 files changed

+32
-35
lines changed

2 files changed

+32
-35
lines changed

Diff for: test-requirements.txt

+1
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ pytest-cov
99
pytest-instafail
1010
pytest-mock
1111
pytest-sugar
12+
sumtypes

Diff for: test/test_index.py

+31-35
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
# This module is part of GitPython and is released under the
44
# 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/
55

6-
import enum
76
from io import BytesIO
87
import logging
98
import os
@@ -14,6 +13,7 @@
1413
import tempfile
1514

1615
import pytest
16+
from sumtypes import constructor, sumtype
1717

1818
from git import (
1919
IndexFile,
@@ -39,35 +39,34 @@
3939
log = logging.getLogger(__name__)
4040

4141

42-
@enum.unique
43-
class _WinBashStatus(enum.Enum):
42+
@sumtype
43+
class _WinBashStatus:
4444
"""Status of bash.exe for native Windows. Affects which commit hook tests can pass.
4545
4646
Call :meth:`check` to check the status.
4747
"""
4848

49-
INAPPLICABLE = enum.auto()
49+
Inapplicable = constructor()
5050
"""This system is not native Windows: either not Windows at all, or Cygwin."""
5151

52-
ABSENT = enum.auto()
52+
Absent = constructor()
5353
"""No command for ``bash.exe`` is found on the system."""
5454

55-
NATIVE = enum.auto()
55+
Native = constructor()
5656
"""Running ``bash.exe`` operates outside any WSL distribution (as with Git Bash)."""
5757

58-
WSL = enum.auto()
58+
Wsl = constructor()
5959
"""Running ``bash.exe`` calls ``bash`` in a WSL distribution."""
6060

61-
WSL_NO_DISTRO = enum.auto()
61+
WslNoDistro = constructor()
6262
"""Running ``bash.exe` tries to run bash on a WSL distribution, but none exists."""
6363

64-
ERROR_WHILE_CHECKING = enum.auto()
64+
ErrorWhileChecking = constructor("error_or_process")
6565
"""Could not determine the status.
6666
6767
This should not trigger a skip or xfail, as it typically indicates either a fixable
6868
problem on the test machine, such as an "Insufficient system resources exist to
6969
complete the requested service" error starting WSL, or a bug in this detection code.
70-
``ERROR_WHILE_CHECKING.error_or_process`` has details about the most recent failure.
7170
"""
7271

7372
@classmethod
@@ -93,7 +92,13 @@ def check(cls):
9392
administrators occasionally put executables there in lieu of extending ``PATH``.
9493
"""
9594
if os.name != "nt":
96-
return cls.INAPPLICABLE
95+
return cls.Inapplicable()
96+
97+
no_distro_message = "Windows Subsystem for Linux has no installed distributions."
98+
99+
def error_running_bash(error):
100+
log.error("Error running bash.exe to check WSL status: %s", error)
101+
return cls.ErrorWhileChecking(error)
97102

98103
try:
99104
# Output rather than forwarding the test command's exit status so that if a
@@ -103,30 +108,21 @@ def check(cls):
103108
command = ["bash.exe", "-c", script]
104109
proc = subprocess.run(command, capture_output=True, check=True, text=True)
105110
except FileNotFoundError:
106-
return cls.ABSENT
111+
return cls.Absent()
107112
except OSError as error:
108-
return cls._error(error)
113+
return error_running_bash(error)
109114
except subprocess.CalledProcessError as error:
110-
no_distro_message = "Windows Subsystem for Linux has no installed distributions."
111115
if error.returncode == 1 and error.stdout.startswith(no_distro_message):
112-
return cls.WSL_NO_DISTRO
113-
return cls._error(error)
116+
return cls.WslNoDistro()
117+
return error_running_bash(error)
114118

115119
status = proc.stdout.rstrip()
116120
if status == "0":
117-
return cls.WSL
121+
return cls.Wsl()
118122
if status == "1":
119-
return cls.NATIVE
120-
return cls._error(proc)
121-
122-
@classmethod
123-
def _error(cls, error_or_process):
124-
if isinstance(error_or_process, subprocess.CompletedProcess):
125-
log.error("Strange output checking WSL status: %s", error_or_process.stdout)
126-
else:
127-
log.error("Error running bash.exe to check WSL status: %s", error_or_process)
128-
cls.ERROR_WHILE_CHECKING.error_or_process = error_or_process
129-
return cls.ERROR_WHILE_CHECKING
123+
return cls.Native()
124+
log.error("Strange output checking WSL status: %s", proc.stdout)
125+
return cls.ErrorWhileChecking(proc)
130126

131127

132128
_win_bash_status = _WinBashStatus.check()
@@ -1001,7 +997,7 @@ class Mocked:
1001997
self.assertEqual(rel, os.path.relpath(path, root))
1002998

1003999
@pytest.mark.xfail(
1004-
_win_bash_status is _WinBashStatus.WSL_NO_DISTRO,
1000+
type(_win_bash_status) is _WinBashStatus.WslNoDistro,
10051001
reason="Currently uses the bash.exe for WSL even with no WSL distro installed",
10061002
raises=HookExecutionError,
10071003
)
@@ -1012,7 +1008,7 @@ def test_pre_commit_hook_success(self, rw_repo):
10121008
index.commit("This should not fail")
10131009

10141010
@pytest.mark.xfail(
1015-
_win_bash_status is _WinBashStatus.WSL_NO_DISTRO,
1011+
type(_win_bash_status) is _WinBashStatus.WslNoDistro,
10161012
reason="Currently uses the bash.exe for WSL even with no WSL distro installed",
10171013
raises=AssertionError,
10181014
)
@@ -1023,7 +1019,7 @@ def test_pre_commit_hook_fail(self, rw_repo):
10231019
try:
10241020
index.commit("This should fail")
10251021
except HookExecutionError as err:
1026-
if _win_bash_status is _WinBashStatus.ABSENT:
1022+
if type(_win_bash_status) is _WinBashStatus.Absent:
10271023
self.assertIsInstance(err.status, OSError)
10281024
self.assertEqual(err.command, [hp])
10291025
self.assertEqual(err.stdout, "")
@@ -1039,12 +1035,12 @@ def test_pre_commit_hook_fail(self, rw_repo):
10391035
raise AssertionError("Should have caught a HookExecutionError")
10401036

10411037
@pytest.mark.xfail(
1042-
_win_bash_status in {_WinBashStatus.ABSENT, _WinBashStatus.WSL},
1038+
type(_win_bash_status) in {_WinBashStatus.Absent, _WinBashStatus.Wsl},
10431039
reason="Specifically seems to fail on WSL bash (in spite of #1399)",
10441040
raises=AssertionError,
10451041
)
10461042
@pytest.mark.xfail(
1047-
_win_bash_status is _WinBashStatus.WSL_NO_DISTRO,
1043+
type(_win_bash_status) is _WinBashStatus.WslNoDistro,
10481044
reason="Currently uses the bash.exe for WSL even with no WSL distro installed",
10491045
raises=HookExecutionError,
10501046
)
@@ -1062,7 +1058,7 @@ def test_commit_msg_hook_success(self, rw_repo):
10621058
self.assertEqual(new_commit.message, "{} {}".format(commit_message, from_hook_message))
10631059

10641060
@pytest.mark.xfail(
1065-
_win_bash_status is _WinBashStatus.WSL_NO_DISTRO,
1061+
type(_win_bash_status) is _WinBashStatus.WslNoDistro,
10661062
reason="Currently uses the bash.exe for WSL even with no WSL distro installed",
10671063
raises=AssertionError,
10681064
)
@@ -1073,7 +1069,7 @@ def test_commit_msg_hook_fail(self, rw_repo):
10731069
try:
10741070
index.commit("This should fail")
10751071
except HookExecutionError as err:
1076-
if _win_bash_status is _WinBashStatus.ABSENT:
1072+
if type(_win_bash_status) is _WinBashStatus.Absent:
10771073
self.assertIsInstance(err.status, OSError)
10781074
self.assertEqual(err.command, [hp])
10791075
self.assertEqual(err.stdout, "")

0 commit comments

Comments
 (0)