log : refactored acquire and release semantic to meet the context manager protocol

This commit is contained in:
alalazo 2016-07-14 09:34:01 +02:00
parent 00b8e0b567
commit 1ecea4c2f1
2 changed files with 80 additions and 73 deletions

View File

@ -101,19 +101,23 @@ def __exit__(self, exc_type, exception, traceback):
class log_output(object):
"""Redirects output and error of enclosed block to a file.
"""Spawns a daemon that reads from a pipe and writes to a file
Usage:
with log_output('logfile.txt', 'w'):
# do things ... output will be logged.
# Spawns the daemon
with log_output('logfile.txt', 'w') as log_redirection:
# do things ... output is not redirected
with log_redirection:
# do things ... output will be logged
or:
with log_output('logfile.txt', echo=True):
# do things ... output will be logged
# and also printed to stdout.
with log_output('logfile.txt', echo=True) as log_redirection:
# do things ... output is not redirected
with log_redirection:
# do things ... output will be logged
# and also printed to stdout.
Opens a stream in 'w' mode at instance creation and closes
it at instance deletion
Opens a stream in 'w' mode at daemon spawning and closes it at daemon joining.
If echo is True, also prints the output to stdout.
"""
def __init__(self, filename, echo=False, force_color=False, debug=False):
@ -128,32 +132,21 @@ def __init__(self, filename, echo=False, force_color=False, debug=False):
self.directAssignment = False
self.read, self.write = os.pipe()
# Spawn a daemon that writes what it reads from a pipe
self.p = multiprocessing.Process(target=self._forward_redirected_pipe, args=(self.read,), name='logger_daemon')
# Sets a daemon that writes to file what it reads from a pipe
self.p = multiprocessing.Process(target=self._spawn_writing_daemon, args=(self.read,), name='logger_daemon')
self.p.daemon = True
# I just need this to communicate to un-summon the daemon
# Needed to un-summon the daemon
self.parent_pipe, self.child_pipe = multiprocessing.Pipe()
def acquire(self):
def __enter__(self):
self.p.start()
return log_output.OutputRedirection(self)
def release(self):
def __exit__(self, exc_type, exc_val, exc_tb):
self.parent_pipe.send(True)
self.p.join(60.0) # 1 minute to join the child
def __enter__(self):
"""Redirect output from the with block to a file.
Hijacks stdout / stderr and writes to the pipe
connected to the logger daemon
"""
# remember these values for later.
self._force_color = color._force_color
self._debug = tty._debug
# Redirect this output to a pipe
self._redirect_to_pipe(self.write)
def _forward_redirected_pipe(self, read):
def _spawn_writing_daemon(self, read):
# Parent: read from child, skip the with block.
read_file = os.fdopen(read, 'r', 0)
with open(self.filename, 'w') as log_file:
@ -187,47 +180,61 @@ def _forward_redirected_pipe(self, read):
if self.child_pipe.poll():
break
def _redirect_to_pipe(self, write):
try:
# Save old stdout and stderr
self._stdout = os.dup(sys.stdout.fileno())
self._stderr = os.dup(sys.stderr.fileno())
# redirect to the pipe.
os.dup2(write, sys.stdout.fileno())
os.dup2(write, sys.stderr.fileno())
except AttributeError:
self.directAssignment = True
self._stdout = sys.stdout
self._stderr = sys.stderr
output_redirect = os.fdopen(write, 'w')
sys.stdout = output_redirect
sys.stderr = output_redirect
if self.force_color:
color._force_color = True
if self.debug:
tty._debug = True
def __exit__(self, exc_type, exception, traceback):
"""Plugs back the original file descriptors
for stdout and stderr
"""
# Flush the log to disk.
sys.stdout.flush()
sys.stderr.flush()
if self.directAssignment:
# We seem to need this only to pass test/install.py
sys.stdout = self._stdout
sys.stderr = self._stderr
else:
os.dup2(self._stdout, sys.stdout.fileno())
os.dup2(self._stderr, sys.stderr.fileno())
# restore output options.
color._force_color = self._force_color
tty._debug = self._debug
def __del__(self):
"""Closes the pipes"""
os.close(self.write)
os.close(self.read)
class OutputRedirection(object):
def __init__(self, other):
self.__dict__.update(other.__dict__)
def __enter__(self):
"""Redirect output from the with block to a file.
Hijacks stdout / stderr and writes to the pipe
connected to the logger daemon
"""
# remember these values for later.
self._force_color = color._force_color
self._debug = tty._debug
# Redirect this output to a pipe
write = self.write
try:
# Save old stdout and stderr
self._stdout = os.dup(sys.stdout.fileno())
self._stderr = os.dup(sys.stderr.fileno())
# redirect to the pipe.
os.dup2(write, sys.stdout.fileno())
os.dup2(write, sys.stderr.fileno())
except AttributeError:
self.directAssignment = True
self._stdout = sys.stdout
self._stderr = sys.stderr
output_redirect = os.fdopen(write, 'w')
sys.stdout = output_redirect
sys.stderr = output_redirect
if self.force_color:
color._force_color = True
if self.debug:
tty._debug = True
def __exit__(self, exc_type, exception, traceback):
"""Plugs back the original file descriptors
for stdout and stderr
"""
# Flush the log to disk.
sys.stdout.flush()
sys.stderr.flush()
if self.directAssignment:
# We seem to need this only to pass test/install.py
sys.stdout = self._stdout
sys.stderr = self._stderr
else:
os.dup2(self._stdout, sys.stdout.fileno())
os.dup2(self._stderr, sys.stderr.fileno())
# restore output options.
color._force_color = self._force_color
tty._debug = self._debug

View File

@ -1136,13 +1136,13 @@ def build_process():
self.log_path = log_path
self.env_path = env_path
dump_environment(env_path)
log_redirection = log_output(log_path, verbose, sys.stdout.isatty(), True)
log_redirection.acquire()
for phase_name, phase in zip(self.phases, self._InstallPhase_phases):
tty.msg('Executing phase : \'{0}\''.format(phase_name))
with log_redirection:
getattr(self, phase)(self.spec, self.prefix)
log_redirection.release()
# Spawn a daemon that reads from a pipe and redirects everything to log_path
with log_output(log_path, verbose, sys.stdout.isatty(), True) as log_redirection:
for phase_name, phase in zip(self.phases, self._InstallPhase_phases):
tty.msg('Executing phase : \'{0}\''.format(phase_name))
# Redirect stdout and stderr to daemon pipe
with log_redirection:
getattr(self, phase)(self.spec, self.prefix)
self.log()
# Run post install hooks before build stage is removed.
spack.hooks.post_install(self)