Mercurial > hg > mercurial-source
annotate hgext/progress.py @ 10464:149ad0a3ec91 stable
progress: make progress.refresh=0 always display the progress line
This has no effect in real world where progress.refresh in unlikely to be set
to zero, but is very useful in tests where all progress output is to be traced.
It failed on platforms with coarse time.time() granularity, like Windows+py25.
author | Patrick Mezard <pmezard@gmail.com> |
---|---|
date | Sun, 14 Feb 2010 15:42:47 +0100 (2010-02-14) |
parents | 5ddde896a19d |
children | 132eb7128ad5 |
rev | line source |
---|---|
10435 | 1 # progress.py show progress bars for some actions |
2 # | |
3 # Copyright (C) 2010 Augie Fackler <durin42@gmail.com> | |
4 # | |
5 # This program is free software; you can redistribute it and/or modify it | |
6 # under the terms of the GNU General Public License as published by the | |
7 # Free Software Foundation; either version 2 of the License, or (at your | |
8 # option) any later version. | |
9 # | |
10 # This program is distributed in the hope that it will be useful, but | |
11 # WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General | |
13 # Public License for more details. | |
14 # | |
15 # You should have received a copy of the GNU General Public License along | |
16 # with this program; if not, write to the Free Software Foundation, Inc., | |
17 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
18 | |
19 """show progress bars for some actions | |
20 | |
10450 | 21 This extension uses the progress information logged by hg commands |
22 to draw progress bars that are as informative as possible. Some progress | |
10435 | 23 bars only offer indeterminate information, while others have a definite |
24 end point. | |
25 | |
26 The following settings are available:: | |
27 | |
28 [progress] | |
29 delay = 3 # number of seconds (float) before showing the progress bar | |
30 refresh = 0.1 # time in seconds between refreshes of the progress bar | |
31 format = topic bar number # format of the progress bar | |
32 width = <none> # if set, the maximum width of the progress information | |
33 # (that is, min(width, term width) will be used) | |
34 clear-complete = True # clear the progress bar after it's done | |
35 | |
36 Valid entries for the format field are topic, bar, number, unit, and item. | |
37 item defaults to the last 20 characters of the item, but this can be | |
38 changed by adding either -<num> which would take the last num characters, | |
39 or +<num> for the first num characters. | |
40 """ | |
41 | |
42 import sys | |
43 import time | |
44 | |
45 from mercurial import extensions | |
46 from mercurial import util | |
47 | |
48 def spacejoin(*args): | |
10452
59f8fff4f887
progress: simplify spacejoin()
Brodie Rao <me+hg@dackz.net>
parents:
10450
diff
changeset
|
49 return ' '.join(s for s in args if s) |
10435 | 50 |
51 class progbar(object): | |
52 def __init__(self, ui): | |
53 self.ui = ui | |
54 self.resetstate() | |
55 | |
56 def resetstate(self): | |
57 self.topics = [] | |
58 self.printed = False | |
59 self.lastprint = time.time() + float(self.ui.config( | |
60 'progress', 'delay', default=3)) | |
61 self.indetcount = 0 | |
62 self.refresh = float(self.ui.config( | |
63 'progress', 'refresh', default=0.1)) | |
64 self.order = self.ui.configlist( | |
65 'progress', 'format', | |
66 default=['topic', 'bar', 'number']) | |
67 | |
68 def show(self, topic, pos, item, unit, total): | |
69 termwidth = self.width() | |
70 self.printed = True | |
71 head = '' | |
72 needprogress = False | |
73 tail = '' | |
74 for indicator in self.order: | |
75 add = '' | |
76 if indicator == 'topic': | |
77 add = topic | |
78 elif indicator == 'number': | |
79 if total: | |
80 add = ('% ' + str(len(str(total))) + | |
81 's/%s') % (pos, total) | |
82 else: | |
83 add = str(pos) | |
84 elif indicator.startswith('item') and item: | |
85 slice = 'end' | |
86 if '-' in indicator: | |
87 wid = int(indicator.split('-')[1]) | |
88 elif '+' in indicator: | |
89 slice = 'beginning' | |
90 wid = int(indicator.split('+')[1]) | |
91 else: | |
92 wid = 20 | |
93 if slice == 'end': | |
94 add = item[-wid:] | |
95 else: | |
96 add = item[:wid] | |
97 add += (wid - len(add)) * ' ' | |
98 elif indicator == 'bar': | |
99 add = '' | |
100 needprogress = True | |
101 elif indicator == 'unit' and unit: | |
102 add = unit | |
103 if not needprogress: | |
104 head = spacejoin(head, add) | |
105 else: | |
106 tail = spacejoin(add, tail) | |
107 if needprogress: | |
108 used = 0 | |
109 if head: | |
110 used += len(head) + 1 | |
111 if tail: | |
112 used += len(tail) + 1 | |
113 progwidth = termwidth - used - 3 | |
114 if total: | |
115 amt = pos * progwidth // total | |
10453
7edc649f9f7e
progress: make determinate bar more like wget progress bar
Brodie Rao <me+hg@dackz.net>
parents:
10452
diff
changeset
|
116 bar = '=' * (amt - 1) |
7edc649f9f7e
progress: make determinate bar more like wget progress bar
Brodie Rao <me+hg@dackz.net>
parents:
10452
diff
changeset
|
117 if amt > 0: |
7edc649f9f7e
progress: make determinate bar more like wget progress bar
Brodie Rao <me+hg@dackz.net>
parents:
10452
diff
changeset
|
118 bar += '>' |
7edc649f9f7e
progress: make determinate bar more like wget progress bar
Brodie Rao <me+hg@dackz.net>
parents:
10452
diff
changeset
|
119 bar += ' ' * (progwidth - amt) |
10435 | 120 else: |
121 progwidth -= 3 | |
122 self.indetcount += 1 | |
123 # mod the count by twice the width so we can make the | |
124 # cursor bounce between the right and left sides | |
125 amt = self.indetcount % (2 * progwidth) | |
126 amt -= progwidth | |
127 bar = (' ' * int(progwidth - abs(amt)) + '<=>' + | |
128 ' ' * int(abs(amt))) | |
129 prog = ''.join(('[', bar , ']')) | |
130 out = spacejoin(head, prog, tail) | |
131 else: | |
132 out = spacejoin(head, tail) | |
133 sys.stdout.write('\r' + out[:termwidth]) | |
134 sys.stdout.flush() | |
135 | |
136 def clear(self): | |
137 sys.stdout.write('\r%s\r' % (' ' * self.width())) | |
138 | |
10439
509f4ed56509
progress: correctly handle empty progress topic
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
10435
diff
changeset
|
139 def complete(self): |
509f4ed56509
progress: correctly handle empty progress topic
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
10435
diff
changeset
|
140 if self.ui.configbool('progress', 'clear-complete', default=True): |
509f4ed56509
progress: correctly handle empty progress topic
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
10435
diff
changeset
|
141 self.clear() |
509f4ed56509
progress: correctly handle empty progress topic
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
10435
diff
changeset
|
142 else: |
509f4ed56509
progress: correctly handle empty progress topic
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
10435
diff
changeset
|
143 sys.stdout.write('\n') |
509f4ed56509
progress: correctly handle empty progress topic
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
10435
diff
changeset
|
144 sys.stdout.flush() |
509f4ed56509
progress: correctly handle empty progress topic
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
10435
diff
changeset
|
145 |
10435 | 146 def width(self): |
147 tw = util.termwidth() | |
148 return min(int(self.ui.config('progress', 'width', default=tw)), tw) | |
149 | |
150 def progress(self, orig, topic, pos, item='', unit='', total=None): | |
10439
509f4ed56509
progress: correctly handle empty progress topic
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
10435
diff
changeset
|
151 if pos is None: |
509f4ed56509
progress: correctly handle empty progress topic
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
10435
diff
changeset
|
152 if self.topics and self.topics[-1] == topic and self.printed: |
509f4ed56509
progress: correctly handle empty progress topic
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
10435
diff
changeset
|
153 self.complete() |
10441
dc0d1ca2d378
progress: only reset state if finishing progress for the current topic
Augie Fackler <durin42@gmail.com>
parents:
10439
diff
changeset
|
154 self.resetstate() |
10439
509f4ed56509
progress: correctly handle empty progress topic
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
10435
diff
changeset
|
155 else: |
509f4ed56509
progress: correctly handle empty progress topic
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
10435
diff
changeset
|
156 if topic not in self.topics: |
509f4ed56509
progress: correctly handle empty progress topic
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
10435
diff
changeset
|
157 self.topics.append(topic) |
509f4ed56509
progress: correctly handle empty progress topic
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
10435
diff
changeset
|
158 now = time.time() |
10464
149ad0a3ec91
progress: make progress.refresh=0 always display the progress line
Patrick Mezard <pmezard@gmail.com>
parents:
10463
diff
changeset
|
159 if (now - self.lastprint >= self.refresh |
149ad0a3ec91
progress: make progress.refresh=0 always display the progress line
Patrick Mezard <pmezard@gmail.com>
parents:
10463
diff
changeset
|
160 and topic == self.topics[-1]): |
10439
509f4ed56509
progress: correctly handle empty progress topic
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
10435
diff
changeset
|
161 self.lastprint = now |
509f4ed56509
progress: correctly handle empty progress topic
Benoit Boissinot <benoit.boissinot@ens-lyon.org>
parents:
10435
diff
changeset
|
162 self.show(topic, pos, item, unit, total) |
10435 | 163 return orig(topic, pos, item=item, unit=unit, total=total) |
164 | |
165 def write(self, orig, *args): | |
166 if self.printed: | |
167 self.clear() | |
168 return orig(*args) | |
169 | |
170 sharedprog = None | |
171 | |
172 def uisetup(ui): | |
173 if ui.interactive() and not ui.debugflag: | |
174 # we instantiate one globally shared progress bar to avoid | |
175 # competing progress bars when multiple UI objects get created | |
176 global sharedprog | |
177 if not sharedprog: | |
178 sharedprog = progbar(ui) | |
179 extensions.wrapfunction(ui, 'progress', sharedprog.progress) | |
180 extensions.wrapfunction(ui, 'write', sharedprog.write) | |
181 | |
182 def reposetup(ui, repo): | |
183 uisetup(repo.ui) |