Commit ff7fe817 authored by Paul McCarthy's avatar Paul McCarthy 🚵
Browse files

RF,BF: No flush in py27. Clean up/standardise Process interface

parent 9b5fe387
...@@ -351,7 +351,7 @@ class Context(object): ...@@ -351,7 +351,7 @@ class Context(object):
else: else:
msg = 'Your administrator password is needed to ' \ msg = 'Your administrator password is needed to ' \
'install FSL [attempt {} of 3]:'.format(attempt + 1) 'install FSL [attempt {} of 3]:'.format(attempt + 1)
printmsg(msg, IMPORTANT, end='', flush=True) printmsg(msg, IMPORTANT, end='')
password = getpass.getpass('') password = getpass.getpass('')
valid = validate_admin_password(password) valid = validate_admin_password(password)
...@@ -568,6 +568,8 @@ class Progress(object): ...@@ -568,6 +568,8 @@ class Progress(object):
if overflow: if overflow:
printmsg(' ', end='') printmsg(' ', end='')
self.spin() self.spin()
else:
printmsg(' ', end='')
printmsg(end='\r') printmsg(end='\r')
...@@ -579,7 +581,7 @@ class Progress(object): ...@@ -579,7 +581,7 @@ class Progress(object):
# os.get_terminal_size added in python # os.get_terminal_size added in python
# 3.3, so we try and call tput instead # 3.3, so we try and call tput instead
try: try:
result = Process.check_output('tput cols') result = Process.check_output('tput cols', log_output=False)
return int(result.strip()) return int(result.strip())
except Exception: except Exception:
return fallback return fallback
...@@ -710,31 +712,38 @@ class Process(object): ...@@ -710,31 +712,38 @@ class Process(object):
""" """
def __init__(self, ctx, cmd, admin, **kwargs): def __init__(self, cmd, admin=False, ctx=None, log_output=True, **kwargs):
"""Run the specified command. Starts threads to capture stdout and """Run the specified command. Starts threads to capture stdout and
stderr. stderr.
:arg ctx: The installer Context. Only used for admin password - can :arg cmd: Command to run - passed directly to subprocess.Popen
be None if admin is False. :arg admin: Run the command with administrative privileges
:arg cmd: Command to run - passed directly to subprocess.Popen :arg ctx: The installer Context. Only used for admin password -
:arg admin: Run the command with administrative privileges can be None if admin is False.
:arg kwargs: Passed to subprocess.Popen :arg log_output: If True, the command and all of its stdout/stderr are
logged.
:arg kwargs: Passed to subprocess.Popen
""" """
self.ctx = ctx self.ctx = ctx
self.cmd = cmd self.cmd = cmd
self.admin = admin self.admin = admin
self.stdoutq = queue.Queue() self.log_output = log_output
self.stderrq = queue.Queue() self.stdoutq = queue.Queue()
self.popen = Process.popen(self.cmd, self.admin, self.ctx) self.stderrq = queue.Queue()
if log_output:
log.debug('Running %s [as admin: %s]', cmd, admin)
self.popen = Process.popen(self.cmd, self.admin, self.ctx)
# threads for gathering stdout/stderr # threads for gathering stdout/stderr
self.stdout_thread = threading.Thread( self.stdout_thread = threading.Thread(
target=Process.forward_stream, target=Process.forward_stream,
args=(self.popen, self.stdoutq, cmd, 'stdout')) args=(self.popen, self.stdoutq, cmd, 'stdout', log_output))
self.stderr_thread = threading.Thread( self.stderr_thread = threading.Thread(
target=Process.forward_stream, target=Process.forward_stream,
args=(self.popen, self.stderrq, cmd, 'stderr')) args=(self.popen, self.stderrq, cmd, 'stderr', log_output))
self.stdout_thread.daemon = True self.stdout_thread.daemon = True
self.stderr_thread.daemon = True self.stderr_thread.daemon = True
...@@ -743,20 +752,15 @@ class Process(object): ...@@ -743,20 +752,15 @@ class Process(object):
@staticmethod @staticmethod
def check_output(cmd, admin=False, ctx=None): def check_output(cmd, *args, **kwargs):
"""Behaves like subprocess.check_output. Runs the given command, then """Behaves like subprocess.check_output. Runs the given command, then
waits until it finishes, and return its standard output. An error waits until it finishes, and return its standard output. An error
is raised if the process returns a non-zero exit code. is raised if the process returns a non-zero exit code.
:arg cmd: The command to run, as a string :arg cmd: The command to run, as a string
:arg admin: Whether to run with administrative privileges
:arg ctx: The installer Context object. Only required if admin is
True.
""" """
proc = Process(ctx, cmd, admin=admin) proc = Process(cmd, *args, **kwargs)
proc.popen.wait() proc.popen.wait()
if proc.popen.returncode != 0: if proc.popen.returncode != 0:
...@@ -773,26 +777,21 @@ class Process(object): ...@@ -773,26 +777,21 @@ class Process(object):
@staticmethod @staticmethod
def check_call(cmd, admin=False, ctx=None): def check_call(cmd, *args, **kwargs):
"""Behaves like subprocess.check_call. Runs the given command, then """Behaves like subprocess.check_call. Runs the given command, then
waits until it finishes. An error is raised if the process returns a waits until it finishes. An error is raised if the process returns a
non-zero exit code. non-zero exit code.
:arg cmd: The command to run, as a string :arg cmd: The command to run, as a string
:arg admin: Whether to run with administrative privileges
:arg ctx: The installer Context object. Only required if admin is
True.
""" """
proc = Process(ctx, cmd, admin=admin) proc = Process(cmd, *args, **kwargs)
proc.popen.wait() proc.popen.wait()
if proc.popen.returncode != 0: if proc.popen.returncode != 0:
raise RuntimeError(cmd) raise RuntimeError(cmd)
@staticmethod @staticmethod
def monitor_progress(ctx, cmd, total=None, **kwargs): def monitor_progress(cmd, total=None, *args, **kwargs):
"""Runs the given command, and shows a progress bar under the """Runs the given command, and shows a progress bar under the
assumption that cmd will produce "total" number of lines of output. assumption that cmd will produce "total" number of lines of output.
""" """
...@@ -803,7 +802,7 @@ class Process(object): ...@@ -803,7 +802,7 @@ class Process(object):
fmt='{:.0f}', fmt='{:.0f}',
transform=Progress.percent) as prog: transform=Progress.percent) as prog:
proc = Process(ctx, cmd, **kwargs) proc = Process(cmd, *args, **kwargs)
nlines = 0 nlines = 0
prog.update(nlines, total) prog.update(nlines, total)
...@@ -820,7 +819,7 @@ class Process(object): ...@@ -820,7 +819,7 @@ class Process(object):
@staticmethod @staticmethod
def forward_stream(popen, queue, cmd, streamname): def forward_stream(popen, queue, cmd, streamname, log_output):
"""Reads lines from stream and pushes them onto queue until popen """Reads lines from stream and pushes them onto queue until popen
is finished. Logs every line. is finished. Logs every line.
...@@ -828,12 +827,9 @@ class Process(object): ...@@ -828,12 +827,9 @@ class Process(object):
:arg queue: queue.Queue to push lines onto :arg queue: queue.Queue to push lines onto
:arg cmd: string - the command that is running :arg cmd: string - the command that is running
:arg streamname: string - 'stdout' or 'stderr' :arg streamname: string - 'stdout' or 'stderr'
:arg log_output: If True, log all stdout/stderr.
""" """
# cmd is just used for log messages
if len(cmd) > 50:
cmd = cmd[:50] + '...'
if streamname == 'stdout': stream = popen.stdout if streamname == 'stdout': stream = popen.stdout
else: stream = popen.stderr else: stream = popen.stderr
...@@ -844,13 +840,15 @@ class Process(object): ...@@ -844,13 +840,15 @@ class Process(object):
break break
else: else:
queue.put(line) queue.put(line)
log.debug('%s [%s]: %s', cmd, streamname, line.rstrip()) if log_output:
log.debug('%s [%s]: %s', cmd, streamname, line.rstrip())
# process finished, flush the stream # process finished, flush the stream
line = stream.readline().decode('utf-8') line = stream.readline().decode('utf-8')
while line != '': while line != '':
queue.put(line) queue.put(line)
log.debug('%s [%s]: %s', cmd, streamname, line) if log_output:
log.debug('%s [%s]: %s', cmd, streamname, line)
line = stream.readline().decode('utf-8') line = stream.readline().decode('utf-8')
...@@ -874,8 +872,6 @@ class Process(object): ...@@ -874,8 +872,6 @@ class Process(object):
if admin: password = ctx.password if admin: password = ctx.password
else: password = None else: password = None
log.debug('Running %s [as admin: %s]', cmd, admin)
cmd = shlex.split(cmd) cmd = shlex.split(cmd)
kwargs = dict(stdin=sp.PIPE, stdout=sp.PIPE, stderr=sp.PIPE) kwargs = dict(stdin=sp.PIPE, stdout=sp.PIPE, stderr=sp.PIPE)
...@@ -939,7 +935,7 @@ def install_miniconda(ctx): ...@@ -939,7 +935,7 @@ def install_miniconda(ctx):
# Install # Install
printmsg('Installing miniconda at {}...'.format(ctx.destdir)) printmsg('Installing miniconda at {}...'.format(ctx.destdir))
cmd = 'sh miniforge.sh -b -p {}'.format(ctx.destdir) cmd = 'sh miniforge.sh -b -p {}'.format(ctx.destdir)
Process.monitor_progress(ctx, cmd, output, admin=ctx.need_admin) Process.monitor_progress(cmd, output, ctx.need_admin, ctx)
# Create .condarc config file # Create .condarc config file
condarc = tw.dedent(""" condarc = tw.dedent("""
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment