Skip to content

Commit 1863b7c

Browse files
authored
Merge pull request #2462 from segevfiner/py36-windowsconsoleio-workaround
[WIP] A workaround for Python 3.6 WindowsConsoleIO breaking with FDCapture
2 parents f826b23 + 01ed6df commit 1863b7c

File tree

2 files changed

+53
-1
lines changed

2 files changed

+53
-1
lines changed

_pytest/capture.py

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import contextlib
88
import sys
99
import os
10+
import io
1011
from io import UnsupportedOperation
1112
from tempfile import TemporaryFile
1213

@@ -33,8 +34,10 @@ def pytest_addoption(parser):
3334

3435
@pytest.hookimpl(hookwrapper=True)
3536
def pytest_load_initial_conftests(early_config, parser, args):
36-
_readline_workaround()
3737
ns = early_config.known_args_namespace
38+
if ns.capture == "fd":
39+
_py36_windowsconsoleio_workaround()
40+
_readline_workaround()
3841
pluginmanager = early_config.pluginmanager
3942
capman = CaptureManager(ns.capture)
4043
pluginmanager.register(capman, "capturemanager")
@@ -491,3 +494,49 @@ def _readline_workaround():
491494
import readline # noqa
492495
except ImportError:
493496
pass
497+
498+
499+
def _py36_windowsconsoleio_workaround():
500+
"""
501+
Python 3.6 implemented unicode console handling for Windows. This works
502+
by reading/writing to the raw console handle using
503+
``{Read,Write}ConsoleW``.
504+
505+
The problem is that we are going to ``dup2`` over the stdio file
506+
descriptors when doing ``FDCapture`` and this will ``CloseHandle`` the
507+
handles used by Python to write to the console. Though there is still some
508+
weirdness and the console handle seems to only be closed randomly and not
509+
on the first call to ``CloseHandle``, or maybe it gets reopened with the
510+
same handle value when we suspend capturing.
511+
512+
The workaround in this case will reopen stdio with a different fd which
513+
also means a different handle by replicating the logic in
514+
"Py_lifecycle.c:initstdio/create_stdio".
515+
516+
See https://github.com/pytest-dev/py/issues/103
517+
"""
518+
if not sys.platform.startswith('win32') or sys.version_info[:2] < (3, 6):
519+
return
520+
521+
buffered = hasattr(sys.stdout.buffer, 'raw')
522+
raw_stdout = sys.stdout.buffer.raw if buffered else sys.stdout.buffer
523+
524+
if not isinstance(raw_stdout, io._WindowsConsoleIO):
525+
return
526+
527+
def _reopen_stdio(f, mode):
528+
if not buffered and mode[0] == 'w':
529+
buffering = 0
530+
else:
531+
buffering = -1
532+
533+
return io.TextIOWrapper(
534+
open(os.dup(f.fileno()), mode, buffering),
535+
f.encoding,
536+
f.errors,
537+
f.newlines,
538+
f.line_buffering)
539+
540+
sys.__stdin__ = sys.stdin = _reopen_stdio(sys.stdin, 'rb')
541+
sys.__stdout__ = sys.stdout = _reopen_stdio(sys.stdout, 'wb')
542+
sys.__stderr__ = sys.stderr = _reopen_stdio(sys.stderr, 'wb')

changelog/103.bugfix

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Added a workaround for Python 3.6 WindowsConsoleIO breaking due to Pytests's
2+
FDCapture. Other code using console handles might still be affected by the
3+
very same issue and might require further workarounds/fixes, i.e. colorama.

0 commit comments

Comments
 (0)