summaryrefslogtreecommitdiffstats
path: root/progress.py
diff options
context:
space:
mode:
Diffstat (limited to 'progress.py')
-rw-r--r--progress.py223
1 files changed, 122 insertions, 101 deletions
diff --git a/progress.py b/progress.py
index 526ce6c1..d1a7c543 100644
--- a/progress.py
+++ b/progress.py
@@ -22,115 +22,136 @@ _NOT_TTY = not os.isatty(2)
22# This will erase all content in the current line (wherever the cursor is). 22# This will erase all content in the current line (wherever the cursor is).
23# It does not move the cursor, so this is usually followed by \r to move to 23# It does not move the cursor, so this is usually followed by \r to move to
24# column 0. 24# column 0.
25CSI_ERASE_LINE = '\x1b[2K' 25CSI_ERASE_LINE = "\x1b[2K"
26 26
27# This will erase all content in the current line after the cursor. This is 27# This will erase all content in the current line after the cursor. This is
28# useful for partial updates & progress messages as the terminal can display 28# useful for partial updates & progress messages as the terminal can display
29# it better. 29# it better.
30CSI_ERASE_LINE_AFTER = '\x1b[K' 30CSI_ERASE_LINE_AFTER = "\x1b[K"
31 31
32 32
33def duration_str(total): 33def duration_str(total):
34 """A less noisy timedelta.__str__. 34 """A less noisy timedelta.__str__.
35 35
36 The default timedelta stringification contains a lot of leading zeros and 36 The default timedelta stringification contains a lot of leading zeros and
37 uses microsecond resolution. This makes for noisy output. 37 uses microsecond resolution. This makes for noisy output.
38 """ 38 """
39 hours, rem = divmod(total, 3600) 39 hours, rem = divmod(total, 3600)
40 mins, secs = divmod(rem, 60) 40 mins, secs = divmod(rem, 60)
41 ret = '%.3fs' % (secs,) 41 ret = "%.3fs" % (secs,)
42 if mins: 42 if mins:
43 ret = '%im%s' % (mins, ret) 43 ret = "%im%s" % (mins, ret)
44 if hours: 44 if hours:
45 ret = '%ih%s' % (hours, ret) 45 ret = "%ih%s" % (hours, ret)
46 return ret 46 return ret
47 47
48 48
49class Progress(object): 49class Progress(object):
50 def __init__(self, title, total=0, units='', print_newline=False, delay=True, 50 def __init__(
51 quiet=False): 51 self,
52 self._title = title 52 title,
53 self._total = total 53 total=0,
54 self._done = 0 54 units="",
55 self._start = time() 55 print_newline=False,
56 self._show = not delay 56 delay=True,
57 self._units = units 57 quiet=False,
58 self._print_newline = print_newline 58 ):
59 # Only show the active jobs section if we run more than one in parallel. 59 self._title = title
60 self._show_jobs = False 60 self._total = total
61 self._active = 0 61 self._done = 0
62 62 self._start = time()
63 # When quiet, never show any output. It's a bit hacky, but reusing the 63 self._show = not delay
64 # existing logic that delays initial output keeps the rest of the class 64 self._units = units
65 # clean. Basically we set the start time to years in the future. 65 self._print_newline = print_newline
66 if quiet: 66 # Only show the active jobs section if we run more than one in parallel.
67 self._show = False 67 self._show_jobs = False
68 self._start += 2**32 68 self._active = 0
69 69
70 def start(self, name): 70 # When quiet, never show any output. It's a bit hacky, but reusing the
71 self._active += 1 71 # existing logic that delays initial output keeps the rest of the class
72 if not self._show_jobs: 72 # clean. Basically we set the start time to years in the future.
73 self._show_jobs = self._active > 1 73 if quiet:
74 self.update(inc=0, msg='started ' + name) 74 self._show = False
75 75 self._start += 2**32
76 def finish(self, name): 76
77 self.update(msg='finished ' + name) 77 def start(self, name):
78 self._active -= 1 78 self._active += 1
79 79 if not self._show_jobs:
80 def update(self, inc=1, msg=''): 80 self._show_jobs = self._active > 1
81 self._done += inc 81 self.update(inc=0, msg="started " + name)
82 82
83 if _NOT_TTY or IsTraceToStderr(): 83 def finish(self, name):
84 return 84 self.update(msg="finished " + name)
85 85 self._active -= 1
86 if not self._show: 86
87 if 0.5 <= time() - self._start: 87 def update(self, inc=1, msg=""):
88 self._show = True 88 self._done += inc
89 else: 89
90 return 90 if _NOT_TTY or IsTraceToStderr():
91 91 return
92 if self._total <= 0: 92
93 sys.stderr.write('\r%s: %d,%s' % ( 93 if not self._show:
94 self._title, 94 if 0.5 <= time() - self._start:
95 self._done, 95 self._show = True
96 CSI_ERASE_LINE_AFTER)) 96 else:
97 sys.stderr.flush() 97 return
98 else: 98
99 p = (100 * self._done) / self._total 99 if self._total <= 0:
100 if self._show_jobs: 100 sys.stderr.write(
101 jobs = '[%d job%s] ' % (self._active, 's' if self._active > 1 else '') 101 "\r%s: %d,%s" % (self._title, self._done, CSI_ERASE_LINE_AFTER)
102 else: 102 )
103 jobs = '' 103 sys.stderr.flush()
104 sys.stderr.write('\r%s: %2d%% %s(%d%s/%d%s)%s%s%s%s' % ( 104 else:
105 self._title, 105 p = (100 * self._done) / self._total
106 p, 106 if self._show_jobs:
107 jobs, 107 jobs = "[%d job%s] " % (
108 self._done, self._units, 108 self._active,
109 self._total, self._units, 109 "s" if self._active > 1 else "",
110 ' ' if msg else '', msg, 110 )
111 CSI_ERASE_LINE_AFTER, 111 else:
112 '\n' if self._print_newline else '')) 112 jobs = ""
113 sys.stderr.flush() 113 sys.stderr.write(
114 114 "\r%s: %2d%% %s(%d%s/%d%s)%s%s%s%s"
115 def end(self): 115 % (
116 if _NOT_TTY or IsTraceToStderr() or not self._show: 116 self._title,
117 return 117 p,
118 118 jobs,
119 duration = duration_str(time() - self._start) 119 self._done,
120 if self._total <= 0: 120 self._units,
121 sys.stderr.write('\r%s: %d, done in %s%s\n' % ( 121 self._total,
122 self._title, 122 self._units,
123 self._done, 123 " " if msg else "",
124 duration, 124 msg,
125 CSI_ERASE_LINE_AFTER)) 125 CSI_ERASE_LINE_AFTER,
126 sys.stderr.flush() 126 "\n" if self._print_newline else "",
127 else: 127 )
128 p = (100 * self._done) / self._total 128 )
129 sys.stderr.write('\r%s: %3d%% (%d%s/%d%s), done in %s%s\n' % ( 129 sys.stderr.flush()
130 self._title, 130
131 p, 131 def end(self):
132 self._done, self._units, 132 if _NOT_TTY or IsTraceToStderr() or not self._show:
133 self._total, self._units, 133 return
134 duration, 134
135 CSI_ERASE_LINE_AFTER)) 135 duration = duration_str(time() - self._start)
136 sys.stderr.flush() 136 if self._total <= 0:
137 sys.stderr.write(
138 "\r%s: %d, done in %s%s\n"
139 % (self._title, self._done, duration, CSI_ERASE_LINE_AFTER)
140 )
141 sys.stderr.flush()
142 else:
143 p = (100 * self._done) / self._total
144 sys.stderr.write(
145 "\r%s: %3d%% (%d%s/%d%s), done in %s%s\n"
146 % (
147 self._title,
148 p,
149 self._done,
150 self._units,
151 self._total,
152 self._units,
153 duration,
154 CSI_ERASE_LINE_AFTER,
155 )
156 )
157 sys.stderr.flush()