use pytest stdout/err capture (#22584)

* On windows, write to StringIO without redirection in test cases to avoid conflicting with logger
This commit is contained in:
loulawrence 2021-04-01 15:34:56 -04:00 committed by Peter Scheibel
parent a0164793cb
commit f587a9ce68
3 changed files with 58 additions and 43 deletions

View file

@ -12,11 +12,11 @@
import textwrap
import traceback
from datetime import datetime
from sys import platform as _platform
import six
from six import StringIO
from six.moves import input
from sys import platform as _platform
if _platform != "win32":
import fcntl

View file

@ -8,20 +8,19 @@
from __future__ import unicode_literals
import atexit
import ctypes
import errno
import io
import multiprocessing
import os
import re
import io
import select
import signal
import sys
import ctypes
import traceback
import tempfile
import threading
from threading import Thread
import traceback
from contextlib import contextmanager
from threading import Thread
from types import ModuleType # novm
from typing import Optional # novm
@ -757,6 +756,9 @@ def __init__(self, logfile, echo=False, debug=0, env=None):
self.stdout = StreamWrapper('stdout')
self.stderr = StreamWrapper('stderr')
self._active = False
self._ioflag = False
self.old_stdout = sys.stdout
self.old_stderr = sys.stderr
def __enter__(self):
if self._active:
@ -768,51 +770,59 @@ def __enter__(self):
# Open both write and reading on logfile
if type(self.logfile) == StringIO:
self._ioflag = True
# cannot have two streams on tempfile, so we must make our own
self.writer = open('temp.txt', mode='wb+')
self.reader = open('temp.txt', mode='rb+')
sys.stdout = self.logfile
sys.stderr = self.logfile
else:
self.writer = open(self.logfile, mode='wb+')
self.reader = open(self.logfile, mode='rb+')
# Dup stdout so we can still write to it after redirection
self.echo_writer = open(os.dup(sys.stdout.fileno()), "w")
# Redirect stdout and stderr to write to logfile
self.stderr.redirect_stream(self.writer.fileno())
self.stdout.redirect_stream(self.writer.fileno())
self._kill = threading.Event()
def background_reader(reader, echo_writer, _kill):
# for each line printed to logfile, read it
# if echo: write line to user
while True:
is_killed = _kill.wait(.1)
self.stderr.flush()
self.stdout.flush()
line = reader.readline()
while line:
if self.echo:
self.echo_writer.write('{0}'.format(line.decode()))
self.echo_writer.flush()
# Dup stdout so we can still write to it after redirection
self.echo_writer = open(os.dup(sys.stdout.fileno()), "w")
# Redirect stdout and stderr to write to logfile
self.stderr.redirect_stream(self.writer.fileno())
self.stdout.redirect_stream(self.writer.fileno())
self._kill = threading.Event()
def background_reader(reader, echo_writer, _kill):
# for each line printed to logfile, read it
# if echo: write line to user
while True:
is_killed = _kill.wait(.1)
self.stderr.flush()
self.stdout.flush()
line = reader.readline()
while line:
if self.echo:
self.echo_writer.write('{0}'.format(line.decode()))
self.echo_writer.flush()
line = reader.readline()
if is_killed:
break
if is_killed:
break
self._active = True
with replace_environment(self.env):
self._thread = Thread(target=background_reader, args=(self.reader, self.echo_writer, self._kill))
self._thread.start()
self._active = True
with replace_environment(self.env):
self._thread = Thread(target=background_reader,
args=(self.reader, self.echo_writer, self._kill))
self._thread.start()
def __exit__(self, exc_type, exc_val, exc_tb):
self.echo_writer.flush()
self.stdout.flush()
self.stderr.flush()
self._kill.set()
self._thread.join()
self.stdout.close()
self.stderr.close()
if os.path.exists("temp.txt"):
os.remove("temp.txt")
if self._ioflag:
sys.stdout = self.old_stdout
sys.stderr = self.old_stderr
self._ioflag = False
else:
self.writer.close()
self.reader.close()
self.echo_writer.flush()
self.stdout.flush()
self.stderr.flush()
self._kill.set()
self._thread.join()
self.stdout.close()
self.stderr.close()
self._active = False
@contextmanager
@ -835,7 +845,6 @@ def force_echo(self):
sys.stdout.flush()
def _writer_daemon(stdin_multiprocess_fd, read_multiprocess_fd, write_fd, echo,
log_file_wrapper, control_pipe, filter_fn):
"""Daemon used by ``log_output`` to write to a log file and to ``stdout``.

View file

@ -19,7 +19,6 @@
import re
import signal
import sys
import termios
import time
import traceback
@ -27,6 +26,13 @@
from spack.util.executable import which
termios = None
try:
import termios as term_mod
termios = term_mod
except ImportError:
pass
class ProcessController(object):
"""Wrapper around some fundamental process control operations.