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

test-requirements.txt

Lines changed: 1 addition & 0 deletions
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

test/test_index.py

Lines changed: 31 additions & 35 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy