diff options
Diffstat (limited to 'progress.py')
| -rw-r--r-- | progress.py | 90 |
1 files changed, 64 insertions, 26 deletions
diff --git a/progress.py b/progress.py index d2ed4bae..43c7ad21 100644 --- a/progress.py +++ b/progress.py | |||
| @@ -1,5 +1,3 @@ | |||
| 1 | # -*- coding:utf-8 -*- | ||
| 2 | # | ||
| 3 | # Copyright (C) 2009 The Android Open Source Project | 1 | # Copyright (C) 2009 The Android Open Source Project |
| 4 | # | 2 | # |
| 5 | # Licensed under the Apache License, Version 2.0 (the "License"); | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| @@ -26,18 +24,53 @@ _NOT_TTY = not os.isatty(2) | |||
| 26 | # column 0. | 24 | # column 0. |
| 27 | CSI_ERASE_LINE = '\x1b[2K' | 25 | CSI_ERASE_LINE = '\x1b[2K' |
| 28 | 26 | ||
| 27 | |||
| 28 | def duration_str(total): | ||
| 29 | """A less noisy timedelta.__str__. | ||
| 30 | |||
| 31 | The default timedelta stringification contains a lot of leading zeros and | ||
| 32 | uses microsecond resolution. This makes for noisy output. | ||
| 33 | """ | ||
| 34 | hours, rem = divmod(total, 3600) | ||
| 35 | mins, secs = divmod(rem, 60) | ||
| 36 | ret = '%.3fs' % (secs,) | ||
| 37 | if mins: | ||
| 38 | ret = '%im%s' % (mins, ret) | ||
| 39 | if hours: | ||
| 40 | ret = '%ih%s' % (hours, ret) | ||
| 41 | return ret | ||
| 42 | |||
| 43 | |||
| 29 | class Progress(object): | 44 | class Progress(object): |
| 30 | def __init__(self, title, total=0, units='', print_newline=False, | 45 | def __init__(self, title, total=0, units='', print_newline=False, delay=True, |
| 31 | always_print_percentage=False): | 46 | quiet=False): |
| 32 | self._title = title | 47 | self._title = title |
| 33 | self._total = total | 48 | self._total = total |
| 34 | self._done = 0 | 49 | self._done = 0 |
| 35 | self._lastp = -1 | ||
| 36 | self._start = time() | 50 | self._start = time() |
| 37 | self._show = False | 51 | self._show = not delay |
| 38 | self._units = units | 52 | self._units = units |
| 39 | self._print_newline = print_newline | 53 | self._print_newline = print_newline |
| 40 | self._always_print_percentage = always_print_percentage | 54 | # Only show the active jobs section if we run more than one in parallel. |
| 55 | self._show_jobs = False | ||
| 56 | self._active = 0 | ||
| 57 | |||
| 58 | # When quiet, never show any output. It's a bit hacky, but reusing the | ||
| 59 | # existing logic that delays initial output keeps the rest of the class | ||
| 60 | # clean. Basically we set the start time to years in the future. | ||
| 61 | if quiet: | ||
| 62 | self._show = False | ||
| 63 | self._start += 2**32 | ||
| 64 | |||
| 65 | def start(self, name): | ||
| 66 | self._active += 1 | ||
| 67 | if not self._show_jobs: | ||
| 68 | self._show_jobs = self._active > 1 | ||
| 69 | self.update(inc=0, msg='started ' + name) | ||
| 70 | |||
| 71 | def finish(self, name): | ||
| 72 | self.update(msg='finished ' + name) | ||
| 73 | self._active -= 1 | ||
| 41 | 74 | ||
| 42 | def update(self, inc=1, msg=''): | 75 | def update(self, inc=1, msg=''): |
| 43 | self._done += inc | 76 | self._done += inc |
| @@ -53,41 +86,46 @@ class Progress(object): | |||
| 53 | 86 | ||
| 54 | if self._total <= 0: | 87 | if self._total <= 0: |
| 55 | sys.stderr.write('%s\r%s: %d,' % ( | 88 | sys.stderr.write('%s\r%s: %d,' % ( |
| 56 | CSI_ERASE_LINE, | 89 | CSI_ERASE_LINE, |
| 57 | self._title, | 90 | self._title, |
| 58 | self._done)) | 91 | self._done)) |
| 59 | sys.stderr.flush() | 92 | sys.stderr.flush() |
| 60 | else: | 93 | else: |
| 61 | p = (100 * self._done) / self._total | 94 | p = (100 * self._done) / self._total |
| 62 | 95 | if self._show_jobs: | |
| 63 | if self._lastp != p or self._always_print_percentage: | 96 | jobs = '[%d job%s] ' % (self._active, 's' if self._active > 1 else '') |
| 64 | self._lastp = p | 97 | else: |
| 65 | sys.stderr.write('%s\r%s: %3d%% (%d%s/%d%s)%s%s%s' % ( | 98 | jobs = '' |
| 99 | sys.stderr.write('%s\r%s: %2d%% %s(%d%s/%d%s)%s%s%s' % ( | ||
| 66 | CSI_ERASE_LINE, | 100 | CSI_ERASE_LINE, |
| 67 | self._title, | 101 | self._title, |
| 68 | p, | 102 | p, |
| 103 | jobs, | ||
| 69 | self._done, self._units, | 104 | self._done, self._units, |
| 70 | self._total, self._units, | 105 | self._total, self._units, |
| 71 | ' ' if msg else '', msg, | 106 | ' ' if msg else '', msg, |
| 72 | "\n" if self._print_newline else "")) | 107 | '\n' if self._print_newline else '')) |
| 73 | sys.stderr.flush() | 108 | sys.stderr.flush() |
| 74 | 109 | ||
| 75 | def end(self): | 110 | def end(self): |
| 76 | if _NOT_TTY or IsTrace() or not self._show: | 111 | if _NOT_TTY or IsTrace() or not self._show: |
| 77 | return | 112 | return |
| 78 | 113 | ||
| 114 | duration = duration_str(time() - self._start) | ||
| 79 | if self._total <= 0: | 115 | if self._total <= 0: |
| 80 | sys.stderr.write('%s\r%s: %d, done.\n' % ( | 116 | sys.stderr.write('%s\r%s: %d, done in %s\n' % ( |
| 81 | CSI_ERASE_LINE, | 117 | CSI_ERASE_LINE, |
| 82 | self._title, | 118 | self._title, |
| 83 | self._done)) | 119 | self._done, |
| 120 | duration)) | ||
| 84 | sys.stderr.flush() | 121 | sys.stderr.flush() |
| 85 | else: | 122 | else: |
| 86 | p = (100 * self._done) / self._total | 123 | p = (100 * self._done) / self._total |
| 87 | sys.stderr.write('%s\r%s: %3d%% (%d%s/%d%s), done.\n' % ( | 124 | sys.stderr.write('%s\r%s: %3d%% (%d%s/%d%s), done in %s\n' % ( |
| 88 | CSI_ERASE_LINE, | 125 | CSI_ERASE_LINE, |
| 89 | self._title, | 126 | self._title, |
| 90 | p, | 127 | p, |
| 91 | self._done, self._units, | 128 | self._done, self._units, |
| 92 | self._total, self._units)) | 129 | self._total, self._units, |
| 130 | duration)) | ||
| 93 | sys.stderr.flush() | 131 | sys.stderr.flush() |
