Repository navigation
test_subprocess: test_getoutput fails on non-English Windows, as text mode decodes cmd.exe's OEM output as UTF-8 #160652
Description
Bug report
test_subprocess.test_getoutput fails on any Windows whose console code page is not UTF-8, because the child's output is decoded as UTF-8. The dependency on the system language can be removed — a child that simply writes non-UTF-8 bytes shows the same thing:
import subprocess, sys
subprocess.run([sys.executable, "-c",
"import sys; sys.stdout.buffer.write('版本'.encode('cp936'))"],
stdout=subprocess.PIPE, text=True).stdout| interpreter | sys.flags.utf8_mode |
result |
|---|---|---|
| 3.14.7 | 0 | '\u7248\u672c' |
| 3.16.0a1 (main) | 1 | UnicodeDecodeError: 'utf-8' codec can't decode byte 0xb0 in position 0 |
3.12.10 with -X utf8=1 |
1 | same UnicodeDecodeError |
Lib/subprocess.py:1063 resolves text mode through _text_encoding(), which returns utf-8 whenever UTF-8 mode is on — the default since 3.15 (PEP 686). The child is not under Python's control: cmd.exe built-ins still write the console's OEM code page (936 here; GetACP() = GetOEMCP() = GetConsoleOutputCP() = 936, while locale.getencoding() stays cp936).
That is what breaks the test: it runs getstatusoutput("type <missing file>") and only asserts a non-zero status, but dies while decoding the localized error message:
File "Lib/subprocess.py", line 1382, in communicate
stdout = self.stdout.read()
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xd5 in position 4: invalid continuation byteOn the same machine, chcp 437 turns the message into ASCII (Access is denied.) and the test passes, and -X utf8=0 makes it pass too — the failure tracks the child's code page, not the build. CPython's Windows CI runs English images, so it stays green.
Suggested fix (test-side): stop depending on the child's localized text, e.g. subprocess.run([sys.executable, "-c", "sys.exit(1)"], stdout=subprocess.PIPE, text=True) and assert the status only.
Two neighbours, both distinct: gh-158006 (with two pipes the decode error stays in the reader thread and stdout becomes None), gh-158633 (the docs don't say the default encoding is a guess about the child).
environment and full traceback
$ ./PCbuild/amd64/python_d.exe -c "import ctypes,locale,sys;k=ctypes.windll.kernel32;print(k.GetACP(),k.GetOEMCP(),k.GetConsoleOutputCP(),locale.getencoding(),locale.getpreferredencoding(False),sys.flags.utf8_mode)"
936 936 936 cp936 utf-8 1
$ ./PCbuild/amd64/python_d.exe -m test -v test_subprocess -m test_getoutput
ERROR: test_getoutput (test.test_subprocess.MiscTests.test_getoutput)
File "Lib/test/test_subprocess.py", line 4135, in test_getoutput
status, output = subprocess.getstatusoutput(
File "Lib/subprocess.py", line 792, in getstatusoutput
data = check_output(cmd, shell=True, text=True, stderr=STDOUT,
File "Lib/subprocess.py", line 1382, in communicate
stdout = self.stdout.read()
File "Lib/codecs.py", line 327, in decode
(result, consumed) = self._buffer_decode(data, self.errors, final)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xd5 in position 4: invalid continuation byteMachine: Windows 11 (10.0.26100), zh-CN, CPython built from heads/main:11598362a3e with MSVC 14.44, Debug x64.
CPython versions tested on:
3.12, 3.14, 3.16, main
Operating systems tested on:
Windows
AI assistance: drafted with deepseek-v4.1-flash in the pi coding agent; every command above was run locally on the machine described, and the text was reviewed by the author before posting.
Activity
aisk commentedon Oct 25, 2026
I can verify this on a Chinese locale Windows machine (code page 936) with current main.
aisk commentedon Oct 25, 2026
Using
sys.exit(1)from the suggested fix would skipgetstatusoutput(), but that is what this test checks. I suggest passingerrors='replace'instead, so the test still uses getstatusoutput().summeriver13 commentedon Oct 25, 2026
Agreed — a
sys.exit(1)child would stop exercisinggetstatusoutput(), which is the point of the test. Opened #160664 witherrors="replace": on CP936 the same call now returns(1, <mojibake>)instead of raising, and-m test test_subprocesspasses (run=371 skipped=216).It needs the
skip newslabel — test-only change, no blurb.