Skip to content

Commit 496acaa

Browse files
committed
Handle multiple encodings for WSL error messages
This affects the test suite only. It improves _WinBashStatus. When bash.exe is the WSL wrapper, and it reports an error that prevents bash in a WSL system from ever actually being run, the message may be encoded in a narrow or multibyte encoding, or may instead be in Windows's native UTF-16LE encoding. This was partly handled before, by assuming the message indicating the absence of any installed WSL distribuions could be interpreted as UTF-8, but matching against bytes and not actually decoding it or other error messages. That presented a few problems, which are rememedied here: - It turns out that this "Windows Subsystem for Linux has no installed distributions" message actually can be in UTF-16LE too. This happens when it is part of a message that also reports a textual error code like: Bash/Service/CreateInstance/GetDefaultDistro/WSL_E_DEFAULT_DISTRO_NOT_FOUND Therefore, narrow-encoding matching was not sufficient. - Logged messages were hard to understand, because they were printing the reprs of bytes objects, which sometimes were UTF-16LE and thus had many "\x00" interspersed in them. - The cases were not broken down elegantly. The ErrorWhileChecking case could hold a CompletedProcess, a CalledProcessError, or an OSError. This is now replaced with a WinError case for the rare scenario where CreateProcessW fails in a completely unexpected way, and a CheckError case for when the exit status or output of bash.exe indicates an error other than one we want to handle. CheckError has two attributes: `process` for the CompletedProcess (CalledProcessError is no longer used, and CompletedProcess has `returncode` and other attributes that provide the same info), and `message` for the decoded output.
1 parent d5ed266 commit 496acaa

File tree

1 file changed

+20
-24
lines changed

1 file changed

+20
-24
lines changed

‎test/test_index.py

+20-24
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,11 @@ class _WinBashStatus:
6161
WslNoDistro = constructor()
6262
"""Running ``bash.exe` tries to run bash on a WSL distribution, but none exists."""
6363

64-
ErrorWhileChecking = constructor("error_or_process")
65-
"""Could not determine the status.
64+
CheckError = constructor("process", "message")
65+
"""Running ``bash.exe`` fails in an unexpected error or gives unexpected output."""
6666

67-
This should not trigger a skip or xfail, as it typically indicates either a fixable
68-
problem on the test machine, such as an "Insufficient system resources exist to
69-
complete the requested service" error starting WSL, or a bug in this detection code.
70-
"""
67+
WinError = constructor("exception")
68+
"""``bash.exe`` may exist but can't run. ``CreateProcessW`` fails unexpectedly."""
7169

7270
@classmethod
7371
def check(cls):
@@ -94,36 +92,34 @@ def check(cls):
9492
if os.name != "nt":
9593
return cls.Inapplicable()
9694

97-
# Use bytes because messages for different WSL errors use different encodings.
98-
no_distro_message = b"Windows Subsystem for Linux has no installed distributions."
99-
100-
def error_running_bash(error):
101-
log.error("Error running bash.exe to check WSL status: %r", error)
102-
return cls.ErrorWhileChecking(error)
95+
no_distro_message = "Windows Subsystem for Linux has no installed distributions."
10396

10497
try:
10598
# Output rather than forwarding the test command's exit status so that if a
10699
# failure occurs before we even get to this point, we will detect it. For
107100
# information on ways to check for WSL, see https://superuser.com/a/1749811.
108101
script = 'test -e /proc/sys/fs/binfmt_misc/WSLInterop; echo "$?"'
109102
command = ["bash.exe", "-c", script]
110-
proc = subprocess.run(command, capture_output=True, check=True)
103+
process = subprocess.run(command, capture_output=True)
111104
except FileNotFoundError:
112105
return cls.Absent()
113106
except OSError as error:
114-
return error_running_bash(error)
115-
except subprocess.CalledProcessError as error:
116-
if error.returncode == 1 and error.stdout.startswith(no_distro_message):
117-
return cls.WslNoDistro()
118-
return error_running_bash(error)
119-
120-
status = proc.stdout.rstrip()
121-
if status == b"0":
107+
return cls.WinError(error)
108+
109+
encoding = "utf-16le" if b"\r\0\n\0" in process.stdout else "utf-8"
110+
text = process.stdout.decode(encoding).rstrip() # stdout includes WSL errors.
111+
112+
if process.returncode == 1 and text.startswith(no_distro_message):
113+
return cls.WslNoDistro()
114+
if process.returncode != 0:
115+
log.error("Error running bash.exe to check WSL status: %s", text)
116+
return cls.CheckError(process, text)
117+
if text == "0":
122118
return cls.Wsl()
123-
if status == b"1":
119+
if text == "1":
124120
return cls.Native()
125-
log.error("Strange output checking WSL status: %r", proc.stdout)
126-
return cls.ErrorWhileChecking(proc)
121+
log.error("Strange output checking WSL status: %s", text)
122+
return cls.CheckError(process, text)
127123

128124

129125
_win_bash_status = _WinBashStatus.check()

0 commit comments

Comments
 (0)