From cc0f2473eb054c7ad3ac687161f634305a458016 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 7 Aug 2017 20:55:28 -0300 Subject: [PATCH] Fix windows console workaround error with non-standard io-streams Fix #2666 --- _pytest/capture.py | 15 +++++++++++---- changelog/2666.bugfix | 3 +++ testing/test_capture.py | 17 +++++++++++++++++ 3 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 changelog/2666.bugfix diff --git a/_pytest/capture.py b/_pytest/capture.py index a4171f0fa..cb5af6fcb 100644 --- a/_pytest/capture.py +++ b/_pytest/capture.py @@ -36,7 +36,7 @@ def pytest_addoption(parser): def pytest_load_initial_conftests(early_config, parser, args): ns = early_config.known_args_namespace if ns.capture == "fd": - _py36_windowsconsoleio_workaround() + _py36_windowsconsoleio_workaround(sys.stdout) _colorama_workaround() _readline_workaround() pluginmanager = early_config.pluginmanager @@ -524,7 +524,7 @@ def _readline_workaround(): pass -def _py36_windowsconsoleio_workaround(): +def _py36_windowsconsoleio_workaround(stream): """ Python 3.6 implemented unicode console handling for Windows. This works by reading/writing to the raw console handle using @@ -541,13 +541,20 @@ def _py36_windowsconsoleio_workaround(): also means a different handle by replicating the logic in "Py_lifecycle.c:initstdio/create_stdio". + :param stream: in practice ``sys.stdout`` or ``sys.stderr``, but given + here as parameter for unittesting purposes. + See https://github.com/pytest-dev/py/issues/103 """ if not sys.platform.startswith('win32') or 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 + # bail out if ``stream`` doesn't seem like a proper ``io`` stream (#2666) + if not hasattr(stream, 'buffer'): + return + + buffered = hasattr(stream.buffer, 'raw') + raw_stdout = stream.buffer.raw if buffered else stream.buffer if not isinstance(raw_stdout, io._WindowsConsoleIO): return diff --git a/changelog/2666.bugfix b/changelog/2666.bugfix new file mode 100644 index 000000000..12369b399 --- /dev/null +++ b/changelog/2666.bugfix @@ -0,0 +1,3 @@ +Fix error on Windows and Python 3.6+ when ``sys.stdout`` has been replaced with +a stream-like object which does not implement the full ``io`` module buffer protocol. In particular this +affects ``pytest-xdist`` users on the aforementioned platform. diff --git a/testing/test_capture.py b/testing/test_capture.py index 4dd5d8e09..eb10f3c07 100644 --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -1140,6 +1140,23 @@ def test_error_attribute_issue555(testdir): reprec.assertoutcome(passed=1) +@pytest.mark.skipif(not sys.platform.startswith('win') and sys.version_info[:2] >= (3, 6), + reason='only py3.6+ on windows') +def test_py36_windowsconsoleio_workaround_non_standard_streams(): + """ + Ensure _py36_windowsconsoleio_workaround function works with objects that + do not implement the full ``io``-based stream protocol, for example execnet channels (#2666). + """ + from _pytest.capture import _py36_windowsconsoleio_workaround + + class DummyStream: + def write(self, s): + pass + + stream = DummyStream() + _py36_windowsconsoleio_workaround(stream) + + def test_dontreadfrominput_has_encoding(testdir): testdir.makepyfile(""" import sys