From 9517c3a2aa2418b226ee7ef0675791d96aa23b2e Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Tue, 13 Mar 2018 17:41:26 -0300 Subject: [PATCH] Suppress ``IOError`` when closing the temporary file used for capturing streams in Python 2.7. Fix #2370 --- _pytest/capture.py | 15 +++++++++++++-- changelog/2370.bugfix.rst | 1 + testing/test_capture.py | 24 ++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 changelog/2370.bugfix.rst diff --git a/_pytest/capture.py b/_pytest/capture.py index 36658acce6f..fc429e0732d 100644 --- a/_pytest/capture.py +++ b/_pytest/capture.py @@ -476,7 +476,7 @@ def done(self): os.dup2(targetfd_save, self.targetfd) os.close(targetfd_save) self.syscapture.done() - self.tmpfile.close() + _attempt_to_close_capture_file(self.tmpfile) def suspend(self): self.syscapture.suspend() @@ -530,7 +530,7 @@ def snap(self): def done(self): setattr(sys, self.name, self._old) del self._old - self.tmpfile.close() + _attempt_to_close_capture_file(self.tmpfile) def suspend(self): setattr(sys, self.name, self._old) @@ -681,3 +681,14 @@ def _reopen_stdio(f, mode): 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') + + +def _attempt_to_close_capture_file(f): + """Suppress IOError when closing the temporary file used for capturing streams in py27 (#2370)""" + if six.PY2: + try: + f.close() + except IOError: + pass + else: + f.close() diff --git a/changelog/2370.bugfix.rst b/changelog/2370.bugfix.rst new file mode 100644 index 00000000000..937acb471b0 --- /dev/null +++ b/changelog/2370.bugfix.rst @@ -0,0 +1 @@ +Suppress ``IOError`` when closing the temporary file used for capturing streams in Python 2.7. diff --git a/testing/test_capture.py b/testing/test_capture.py index 69afa0f9c84..4f9028fca33 100644 --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -1265,6 +1265,30 @@ def test_capattr(): reprec.assertoutcome(passed=1) +def test_crash_on_closing_tmpfile_py27(testdir): + testdir.makepyfile(''' + from __future__ import print_function + import time + import threading + import sys + + def spam(): + f = sys.stderr + while True: + print('.', end='', file=f) + + def test_silly(): + t = threading.Thread(target=spam) + t.daemon = True + t.start() + time.sleep(0.5) + + ''') + result = testdir.runpytest_subprocess() + assert result.ret == 0 + assert 'IOError' not in result.stdout.str() + + def test_pickling_and_unpickling_encoded_file(): # See https://bitbucket.org/pytest-dev/pytest/pull-request/194 # pickle.loads() raises infinite recursion if