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:
parent
a0164793cb
commit
f587a9ce68
@ -12,11 +12,11 @@
|
|||||||
import textwrap
|
import textwrap
|
||||||
import traceback
|
import traceback
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from sys import platform as _platform
|
||||||
|
|
||||||
import six
|
import six
|
||||||
from six import StringIO
|
from six import StringIO
|
||||||
from six.moves import input
|
from six.moves import input
|
||||||
from sys import platform as _platform
|
|
||||||
|
|
||||||
if _platform != "win32":
|
if _platform != "win32":
|
||||||
import fcntl
|
import fcntl
|
||||||
|
@ -8,20 +8,19 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import atexit
|
import atexit
|
||||||
|
import ctypes
|
||||||
import errno
|
import errno
|
||||||
|
import io
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import io
|
|
||||||
import select
|
import select
|
||||||
import signal
|
import signal
|
||||||
import sys
|
import sys
|
||||||
import ctypes
|
|
||||||
import traceback
|
|
||||||
import tempfile
|
|
||||||
import threading
|
import threading
|
||||||
from threading import Thread
|
import traceback
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
from threading import Thread
|
||||||
from types import ModuleType # novm
|
from types import ModuleType # novm
|
||||||
from typing import Optional # 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.stdout = StreamWrapper('stdout')
|
||||||
self.stderr = StreamWrapper('stderr')
|
self.stderr = StreamWrapper('stderr')
|
||||||
self._active = False
|
self._active = False
|
||||||
|
self._ioflag = False
|
||||||
|
self.old_stdout = sys.stdout
|
||||||
|
self.old_stderr = sys.stderr
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
if self._active:
|
if self._active:
|
||||||
@ -768,51 +770,59 @@ def __enter__(self):
|
|||||||
|
|
||||||
# Open both write and reading on logfile
|
# Open both write and reading on logfile
|
||||||
if type(self.logfile) == StringIO:
|
if type(self.logfile) == StringIO:
|
||||||
|
self._ioflag = True
|
||||||
# cannot have two streams on tempfile, so we must make our own
|
# cannot have two streams on tempfile, so we must make our own
|
||||||
self.writer = open('temp.txt', mode='wb+')
|
sys.stdout = self.logfile
|
||||||
self.reader = open('temp.txt', mode='rb+')
|
sys.stderr = self.logfile
|
||||||
else:
|
else:
|
||||||
self.writer = open(self.logfile, mode='wb+')
|
self.writer = open(self.logfile, mode='wb+')
|
||||||
self.reader = open(self.logfile, mode='rb+')
|
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):
|
# Dup stdout so we can still write to it after redirection
|
||||||
# for each line printed to logfile, read it
|
self.echo_writer = open(os.dup(sys.stdout.fileno()), "w")
|
||||||
# if echo: write line to user
|
# Redirect stdout and stderr to write to logfile
|
||||||
while True:
|
self.stderr.redirect_stream(self.writer.fileno())
|
||||||
is_killed = _kill.wait(.1)
|
self.stdout.redirect_stream(self.writer.fileno())
|
||||||
self.stderr.flush()
|
self._kill = threading.Event()
|
||||||
self.stdout.flush()
|
|
||||||
line = reader.readline()
|
def background_reader(reader, echo_writer, _kill):
|
||||||
while line:
|
# for each line printed to logfile, read it
|
||||||
if self.echo:
|
# if echo: write line to user
|
||||||
self.echo_writer.write('{0}'.format(line.decode()))
|
while True:
|
||||||
self.echo_writer.flush()
|
is_killed = _kill.wait(.1)
|
||||||
|
self.stderr.flush()
|
||||||
|
self.stdout.flush()
|
||||||
line = reader.readline()
|
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:
|
if is_killed:
|
||||||
break
|
break
|
||||||
|
|
||||||
self._active = True
|
self._active = True
|
||||||
with replace_environment(self.env):
|
with replace_environment(self.env):
|
||||||
self._thread = Thread(target=background_reader, args=(self.reader, self.echo_writer, self._kill))
|
self._thread = Thread(target=background_reader,
|
||||||
self._thread.start()
|
args=(self.reader, self.echo_writer, self._kill))
|
||||||
|
self._thread.start()
|
||||||
|
|
||||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||||
self.echo_writer.flush()
|
if self._ioflag:
|
||||||
self.stdout.flush()
|
sys.stdout = self.old_stdout
|
||||||
self.stderr.flush()
|
sys.stderr = self.old_stderr
|
||||||
self._kill.set()
|
self._ioflag = False
|
||||||
self._thread.join()
|
else:
|
||||||
self.stdout.close()
|
self.writer.close()
|
||||||
self.stderr.close()
|
self.reader.close()
|
||||||
if os.path.exists("temp.txt"):
|
self.echo_writer.flush()
|
||||||
os.remove("temp.txt")
|
self.stdout.flush()
|
||||||
|
self.stderr.flush()
|
||||||
|
self._kill.set()
|
||||||
|
self._thread.join()
|
||||||
|
self.stdout.close()
|
||||||
|
self.stderr.close()
|
||||||
self._active = False
|
self._active = False
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
@ -835,7 +845,6 @@ def force_echo(self):
|
|||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def _writer_daemon(stdin_multiprocess_fd, read_multiprocess_fd, write_fd, echo,
|
def _writer_daemon(stdin_multiprocess_fd, read_multiprocess_fd, write_fd, echo,
|
||||||
log_file_wrapper, control_pipe, filter_fn):
|
log_file_wrapper, control_pipe, filter_fn):
|
||||||
"""Daemon used by ``log_output`` to write to a log file and to ``stdout``.
|
"""Daemon used by ``log_output`` to write to a log file and to ``stdout``.
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
import re
|
import re
|
||||||
import signal
|
import signal
|
||||||
import sys
|
import sys
|
||||||
import termios
|
|
||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
@ -27,6 +26,13 @@
|
|||||||
|
|
||||||
from spack.util.executable import which
|
from spack.util.executable import which
|
||||||
|
|
||||||
|
termios = None
|
||||||
|
try:
|
||||||
|
import termios as term_mod
|
||||||
|
termios = term_mod
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ProcessController(object):
|
class ProcessController(object):
|
||||||
"""Wrapper around some fundamental process control operations.
|
"""Wrapper around some fundamental process control operations.
|
||||||
|
Loading…
Reference in New Issue
Block a user