diff --git a/fig/progress_stream.py b/fig/progress_stream.py new file mode 100644 index 000000000..b0160f04f --- /dev/null +++ b/fig/progress_stream.py @@ -0,0 +1,81 @@ +import json +import os + + +class StreamOutputError(Exception): + pass + + +def stream_output(output, stream): + is_terminal = hasattr(stream, 'fileno') and os.isatty(stream.fileno()) + all_events = [] + lines = {} + diff = 0 + + for chunk in output: + event = json.loads(chunk) + all_events.append(event) + + if 'progress' in event or 'progressDetail' in event: + image_id = event['id'] + + if image_id in lines: + diff = len(lines) - lines[image_id] + else: + lines[image_id] = len(lines) + stream.write("\n") + diff = 0 + + if is_terminal: + # move cursor up `diff` rows + stream.write("%c[%dA" % (27, diff)) + + print_output_event(event, stream, is_terminal) + + if 'id' in event and is_terminal: + # move cursor back down + stream.write("%c[%dB" % (27, diff)) + + stream.flush() + + return all_events + + +def print_output_event(event, stream, is_terminal): + if 'errorDetail' in event: + raise StreamOutputError(event['errorDetail']['message']) + + terminator = '' + + if is_terminal and 'stream' not in event: + # erase current line + stream.write("%c[2K\r" % 27) + terminator = "\r" + pass + elif 'progressDetail' in event: + return + + if 'time' in event: + stream.write("[%s] " % event['time']) + + if 'id' in event: + stream.write("%s: " % event['id']) + + if 'from' in event: + stream.write("(from %s) " % event['from']) + + status = event.get('status', '') + + if 'progress' in event: + stream.write("%s %s%s" % (status, event['progress'], terminator)) + elif 'progressDetail' in event: + detail = event['progressDetail'] + if 'current' in detail: + percentage = float(detail['current']) / float(detail['total']) * 100 + stream.write('%s (%.1f%%)%s' % (status, percentage, terminator)) + else: + stream.write('%s%s' % (status, terminator)) + elif 'stream' in event: + stream.write("%s%s" % (event['stream'], terminator)) + else: + stream.write("%s%s\n" % (status, terminator)) diff --git a/fig/service.py b/fig/service.py index 21a3ea40d..2fc7125d0 100644 --- a/fig/service.py +++ b/fig/service.py @@ -5,8 +5,8 @@ import logging import re import os import sys -import json from .container import Container +from .progress_stream import stream_output, StreamOutputError log = logging.getLogger(__name__) @@ -343,84 +343,6 @@ class Service(object): return True -class StreamOutputError(Exception): - pass - - -def stream_output(output, stream): - is_terminal = hasattr(stream, 'fileno') and os.isatty(stream.fileno()) - all_events = [] - lines = {} - diff = 0 - - for chunk in output: - event = json.loads(chunk) - all_events.append(event) - - if 'progress' in event or 'progressDetail' in event: - image_id = event['id'] - - if image_id in lines: - diff = len(lines) - lines[image_id] - else: - lines[image_id] = len(lines) - stream.write("\n") - diff = 0 - - if is_terminal: - # move cursor up `diff` rows - stream.write("%c[%dA" % (27, diff)) - - print_output_event(event, stream, is_terminal) - - if 'id' in event and is_terminal: - # move cursor back down - stream.write("%c[%dB" % (27, diff)) - - stream.flush() - - return all_events - -def print_output_event(event, stream, is_terminal): - if 'errorDetail' in event: - raise StreamOutputError(event['errorDetail']['message']) - - terminator = '' - - if is_terminal and 'stream' not in event: - # erase current line - stream.write("%c[2K\r" % 27) - terminator = "\r" - pass - elif 'progressDetail' in event: - return - - if 'time' in event: - stream.write("[%s] " % event['time']) - - if 'id' in event: - stream.write("%s: " % event['id']) - - if 'from' in event: - stream.write("(from %s) " % event['from']) - - status = event.get('status', '') - - if 'progress' in event: - stream.write("%s %s%s" % (status, event['progress'], terminator)) - elif 'progressDetail' in event: - detail = event['progressDetail'] - if 'current' in detail: - percentage = float(detail['current']) / float(detail['total']) * 100 - stream.write('%s (%.1f%%)%s' % (status, percentage, terminator)) - else: - stream.write('%s%s' % (status, terminator)) - elif 'stream' in event: - stream.write("%s%s" % (event['stream'], terminator)) - else: - stream.write("%s%s\n" % (status, terminator)) - - NAME_RE = re.compile(r'^([^_]+)_([^_]+)_(run_)?(\d+)$')