From c007b2bbea3ff39b5fe75db67ce5f96c34bc2c69 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 29 Jul 2017 23:41:51 -0500 Subject: [PATCH 1/6] capture: recover from closed files --- _pytest/capture.py | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/_pytest/capture.py b/_pytest/capture.py index a4171f0fa84..fa0687f46ab 100644 --- a/_pytest/capture.py +++ b/_pytest/capture.py @@ -211,7 +211,8 @@ def readouterr(self): @contextlib.contextmanager def disabled(self): - capmanager = self.request.config.pluginmanager.getplugin('capturemanager') + capmanager = self.request.config.pluginmanager.getplugin( + 'capturemanager') capmanager.suspendcapture_item(self.request.node, "call", in_=True) try: yield @@ -248,7 +249,10 @@ def __init__(self, buffer, encoding): def write(self, obj): if isinstance(obj, unicode): obj = obj.encode(self.encoding, "replace") - self.buffer.write(obj) + try: + self.buffer.write(obj) + except OSError: + self.buffer = open(os.devnull, "wb+") def writelines(self, linelist): data = ''.join(linelist) @@ -371,9 +375,14 @@ def start(self): self.syscapture.start() def snap(self): - f = self.tmpfile - f.seek(0) - res = f.read() + try: + f = self.tmpfile + f.seek(0) + res = f.read() + except OSError: + self.tmpfile = open(os.devnull, "r") + self.tmpfile_fd = self.tmpfile.fileno() + res = '' if res: enc = getattr(f, "encoding", None) if enc and isinstance(res, bytes): @@ -398,7 +407,12 @@ def suspend(self): def resume(self): self.syscapture.resume() - os.dup2(self.tmpfile_fd, self.targetfd) + try: + os.dup2(self.tmpfile_fd, self.targetfd) + except OSError: + self.tmpfile = open(os.devnull, "r") + self.tmpfile_fd = self.tmpfile.fileno() + self.targetfd = open(os.devnull, "r").fileno() def writeorg(self, data): """ write to original file descriptor. """ From 5ae339fd253137fc24932cb985fa2f022601cfa8 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 30 Jul 2017 15:48:31 -0500 Subject: [PATCH 2/6] :news: --- changelog/2633.bugfix | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 changelog/2633.bugfix diff --git a/changelog/2633.bugfix b/changelog/2633.bugfix new file mode 100644 index 00000000000..08c4201c47d --- /dev/null +++ b/changelog/2633.bugfix @@ -0,0 +1,2 @@ +In some cases on windows stderr capturing may fail. Pytest now +attempts to recover from a capturing failure. \ No newline at end of file From 9170bc8590457f9e354996a8009893d72cf26c59 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 30 Jul 2017 15:50:13 -0500 Subject: [PATCH 3/6] test_capture: test close stderr --- testing/test_capture.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/testing/test_capture.py b/testing/test_capture.py index 4dd5d8e099b..387aa8ea777 100644 --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -1159,3 +1159,6 @@ def test_pickling_and_unpickling_enocded_file(): ef = capture.EncodedFile(None, None) ef_as_str = pickle.dumps(ef) pickle.loads(ef_as_str) + +def test_closed_handle(): + sys.stderr.close() From 87e554ece5f362c3b09e0beddae740d60b40a709 Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 31 Jul 2017 12:58:26 -0500 Subject: [PATCH 4/6] try removing closed files --- _pytest/capture.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/_pytest/capture.py b/_pytest/capture.py index fa0687f46ab..50a711c0304 100644 --- a/_pytest/capture.py +++ b/_pytest/capture.py @@ -19,7 +19,6 @@ patchsysdict = {0: 'stdin', 1: 'stdout', 2: 'stderr'} - def pytest_addoption(parser): group = parser.getgroup("general") group._addoption( @@ -397,9 +396,9 @@ def done(self): seeked to position zero. """ targetfd_save = self.__dict__.pop("targetfd_save") os.dup2(targetfd_save, self.targetfd) - os.close(targetfd_save) + # os.close(targetfd_save) self.syscapture.done() - self.tmpfile.close() + # self.tmpfile.close() def suspend(self): self.syscapture.suspend() @@ -446,7 +445,7 @@ def snap(self): def done(self): setattr(sys, self.name, self._old) del self._old - self.tmpfile.close() + # self.tmpfile.close() def suspend(self): setattr(sys, self.name, self._old) From 324268fa8af2bd049ca56ced0bf4387528cd3bc1 Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 31 Jul 2017 13:10:31 -0500 Subject: [PATCH 5/6] impl exc --- _pytest/capture.py | 1 + 1 file changed, 1 insertion(+) diff --git a/_pytest/capture.py b/_pytest/capture.py index 50a711c0304..4d8be51baca 100644 --- a/_pytest/capture.py +++ b/_pytest/capture.py @@ -399,6 +399,7 @@ def done(self): # os.close(targetfd_save) self.syscapture.done() # self.tmpfile.close() + raise Exception("Done method called!") def suspend(self): self.syscapture.suspend() From 046386d8b1c3ece449c367540f27e819bddc9ce9 Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 31 Jul 2017 13:26:08 -0500 Subject: [PATCH 6/6] add exc --- _pytest/capture.py | 1 + 1 file changed, 1 insertion(+) diff --git a/_pytest/capture.py b/_pytest/capture.py index 4d8be51baca..21c72405f3d 100644 --- a/_pytest/capture.py +++ b/_pytest/capture.py @@ -447,6 +447,7 @@ def done(self): setattr(sys, self.name, self._old) del self._old # self.tmpfile.close() + raise Exception("Sys method called!") def suspend(self): setattr(sys, self.name, self._old)