From 05796be21a4421e5f76893afd34140706a71b42b Mon Sep 17 00:00:00 2001 From: Segev Finer Date: Fri, 2 Jun 2017 11:19:03 +0300 Subject: [PATCH 1/3] A workaround for Python 3.6 WindowsConsoleIO breaking with FDCapture Python 3.6 implemented unicode console handling for Windows. This works by reading/writing to the raw console handle using ``{Read,Write}ConsoleW``. The problem is that we are going to ``dup2`` over the stdio file descriptors when doing ``FDCapture`` and this will ``CloseHandle`` the handles used by Python to write to the console. Though there is still some weirdness and the console handle seems to only be closed randomly and not on the first call to ``CloseHandle``, or maybe it gets reopened with the same handle value when we suspend capturing. The workaround in this case will reopen stdio with a different fd which also means a different handle by replicating the logic in "Py_lifecycle.c:initstdio/create_stdio". See https://github.com/pytest-dev/py/issues/103 --- _pytest/capture.py | 51 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/_pytest/capture.py b/_pytest/capture.py index 6bc3fc1f0..b67b5f833 100644 --- a/_pytest/capture.py +++ b/_pytest/capture.py @@ -7,6 +7,7 @@ from __future__ import absolute_import, division, print_function import contextlib import sys import os +import io from io import UnsupportedOperation from tempfile import TemporaryFile @@ -33,8 +34,10 @@ def pytest_addoption(parser): @pytest.hookimpl(hookwrapper=True) def pytest_load_initial_conftests(early_config, parser, args): - _readline_workaround() ns = early_config.known_args_namespace + if ns.capture == "fd": + _py36_windowsconsoleio_workaround() + _readline_workaround() pluginmanager = early_config.pluginmanager capman = CaptureManager(ns.capture) pluginmanager.register(capman, "capturemanager") @@ -491,3 +494,49 @@ def _readline_workaround(): import readline # noqa except ImportError: pass + + +def _py36_windowsconsoleio_workaround(): + """ + Python 3.6 implemented unicode console handling for Windows. This works + by reading/writing to the raw console handle using + ``{Read,Write}ConsoleW``. + + The problem is that we are going to ``dup2`` over the stdio file + descriptors when doing ``FDCapture`` and this will ``CloseHandle`` the + handles used by Python to write to the console. Though there is still some + weirdness and the console handle seems to only be closed randomly and not + on the first call to ``CloseHandle``, or maybe it gets reopened with the + same handle value when we suspend capturing. + + The workaround in this case will reopen stdio with a different fd which + also means a different handle by replicating the logic in + "Py_lifecycle.c:initstdio/create_stdio". + + See https://github.com/pytest-dev/py/issues/103 + """ + if not sys.platform.startswith('win32') and sys.version_info[:2] >= (3, 6): + return + + buffered = hasattr(sys.stdout.buffer, 'raw') + raw_stdout = sys.stdout.buffer.raw if buffered else sys.stdout.buffer + + if not isinstance(raw_stdout, io._WindowsConsoleIO): + return + + def _reopen_stdio(f, mode): + if not buffered and mode[0] == 'w': + buffering = 0 + else: + buffering = -1 + + return io.TextIOWrapper( + open(os.dup(f.fileno()), mode, buffering), + f.encoding, + f.errors, + f.newlines, + f.line_buffering) + + sys.__stdin__ = sys.stdin = _reopen_stdio(sys.stdin, 'rb') + sys.__stdout__ = sys.stdout = _reopen_stdio(sys.stdout, 'wb') + sys.__stderr__ = sys.stderr = _reopen_stdio(sys.stderr, 'wb') From 59b3693988fcc19a9f5d7aa5c31cc7de28fcf6b7 Mon Sep 17 00:00:00 2001 From: Segev Finer Date: Fri, 2 Jun 2017 12:34:26 +0300 Subject: [PATCH 2/3] Fixed wrong if in the WindowsConsoleIO workaround --- _pytest/capture.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_pytest/capture.py b/_pytest/capture.py index b67b5f833..3661f2691 100644 --- a/_pytest/capture.py +++ b/_pytest/capture.py @@ -515,7 +515,7 @@ def _py36_windowsconsoleio_workaround(): See https://github.com/pytest-dev/py/issues/103 """ - if not sys.platform.startswith('win32') and sys.version_info[:2] >= (3, 6): + if not sys.platform.startswith('win32') or sys.version_info[:2] < (3, 6): return buffered = hasattr(sys.stdout.buffer, 'raw') From 01ed6dfc3bd29b2d76d3e9ed079f2f0f648c0179 Mon Sep 17 00:00:00 2001 From: Segev Finer Date: Fri, 2 Jun 2017 12:38:31 +0300 Subject: [PATCH 3/3] Added a changelog entry for the WindowsConsoleIO workaround --- changelog/103.bugfix | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 changelog/103.bugfix diff --git a/changelog/103.bugfix b/changelog/103.bugfix new file mode 100644 index 000000000..e67110b64 --- /dev/null +++ b/changelog/103.bugfix @@ -0,0 +1,3 @@ +Added a workaround for Python 3.6 WindowsConsoleIO breaking due to Pytests's +FDCapture. Other code using console handles might still be affected by the +very same issue and might require further workarounds/fixes, i.e. colorama.