diff options
author | Lars Wirzenius <liw@liw.fi> | 2010-07-07 09:41:19 +1200 |
---|---|---|
committer | Lars Wirzenius <liw@liw.fi> | 2010-07-07 09:41:19 +1200 |
commit | 139e35b55938060d80e04750fc36229635fde587 (patch) | |
tree | fdd8b7ce20622f8df2d52274659895b8c7937410 | |
parent | 56f628bb3d49a52c6e5ee1360b520c6381ebd510 (diff) | |
parent | e6618ad0ed66ed459a55ecd903a5627dacf97915 (diff) | |
download | python-ttystatus-139e35b55938060d80e04750fc36229635fde587.tar.gz |
Merge optimizations.
-rw-r--r-- | example.py | 6 | ||||
-rw-r--r-- | ttystatus/bytesize.py | 13 | ||||
-rw-r--r-- | ttystatus/counter.py | 7 | ||||
-rw-r--r-- | ttystatus/elapsed.py | 10 | ||||
-rw-r--r-- | ttystatus/index.py | 23 | ||||
-rw-r--r-- | ttystatus/index_tests.py | 2 | ||||
-rw-r--r-- | ttystatus/literal.py | 4 | ||||
-rw-r--r-- | ttystatus/messager.py | 9 | ||||
-rw-r--r-- | ttystatus/messager_tests.py | 12 | ||||
-rw-r--r-- | ttystatus/pathname.py | 24 | ||||
-rw-r--r-- | ttystatus/percent.py | 20 | ||||
-rw-r--r-- | ttystatus/progressbar.py | 27 | ||||
-rw-r--r-- | ttystatus/remtime.py | 32 | ||||
-rw-r--r-- | ttystatus/remtime_tests.py | 1 | ||||
-rw-r--r-- | ttystatus/status.py | 14 | ||||
-rw-r--r-- | ttystatus/status_tests.py | 19 | ||||
-rw-r--r-- | ttystatus/string.py | 5 | ||||
-rw-r--r-- | ttystatus/widget.py | 33 |
18 files changed, 183 insertions, 78 deletions
@@ -25,7 +25,8 @@ import ttystatus def main(): ts = ttystatus.TerminalStatus(period=0.1) - ts.add(ttystatus.Literal('Looking for files: ')) + ts.add(ttystatus.ElapsedTime()) + ts.add(ttystatus.Literal(' Looking for files: ')) ts.add(ttystatus.Counter('pathname')) ts.add(ttystatus.Literal(' found, currently in ')) ts.add(ttystatus.Pathname('dirname')) @@ -39,7 +40,8 @@ def main(): pathnames.append(pathname) ts.clear() - ts.add(ttystatus.Literal('Finding symlinks: ')) + ts.add(ttystatus.ElapsedTime()) + ts.add(ttystatus.Literal(' Finding symlinks: ')) ts.add(ttystatus.Counter('symlink')) ts.add(ttystatus.Literal(' found; now at ')) ts.add(ttystatus.Index('pathname', 'pathnames')) diff --git a/ttystatus/bytesize.py b/ttystatus/bytesize.py index 0652b2f..f47d453 100644 --- a/ttystatus/bytesize.py +++ b/ttystatus/bytesize.py @@ -23,9 +23,10 @@ class ByteSize(ttystatus.Widget): def __init__(self, name): self.name = name - self.value = self.format(0) + self.interesting_keys = [name] + self._bytes = 0 - def format(self, bytes): + def format(self): units = ( (1024**4, 2, 'TiB'), (1024**3, 2, 'GiB'), @@ -34,11 +35,11 @@ class ByteSize(ttystatus.Widget): ) for factor, decimals, unit in units: - if bytes >= factor: + if self._bytes >= factor: return '%.*f %s' % (decimals, - float(bytes) / float(factor), + float(self._bytes) / float(factor), unit) - return '%d B' % bytes + return '%d B' % self._bytes def update(self, master, width): - self.value = self.format(master[self.name]) + self._bytes = master[self.name] diff --git a/ttystatus/counter.py b/ttystatus/counter.py index b409114..5d3e11f 100644 --- a/ttystatus/counter.py +++ b/ttystatus/counter.py @@ -25,11 +25,12 @@ class Counter(ttystatus.Widget): self.name = name self.prev = None self.count = 0 - self.value = '0' + self.interesting_keys = [name] + + def format(self): + return str(self.count) def update(self, master, width): if master[self.name] != self.prev: self.prev = master[self.name] self.count += 1 - self.value = str(self.count) - diff --git a/ttystatus/elapsed.py b/ttystatus/elapsed.py index bcea7df..2032e6e 100644 --- a/ttystatus/elapsed.py +++ b/ttystatus/elapsed.py @@ -25,13 +25,15 @@ class ElapsedTime(ttystatus.Widget): def __init__(self): self.started = None - self.value = self.format(0) + self.interesting_keys = None + self.secs = 0 def get_time(self): # pragma: no cover '''Wrapper around time.time() for unit tests to override.''' return time.time() - def format(self, secs): + def format(self): + secs = self.secs hours = secs / 3600 secs %= 3600 mins = secs / 60 @@ -41,6 +43,4 @@ class ElapsedTime(ttystatus.Widget): def update(self, master, width): if self.started is None: self.started = self.get_time() - secs = self.get_time() - self.started - self.value = self.format(secs) - + self.secs = self.get_time() - self.started diff --git a/ttystatus/index.py b/ttystatus/index.py index 2d51189..941165a 100644 --- a/ttystatus/index.py +++ b/ttystatus/index.py @@ -24,17 +24,18 @@ class Index(ttystatus.Widget): def __init__(self, name, listname): self.name = name self.listname = listname - self.value = self.format(0, 0) + self.interesting_keys = [name, listname] + self.value = None + self.listvalue = [] - def format(self, index, listlen): - return '%d/%d' % (index, listlen) - - def update(self, master, width): - value = master[self.name] - listvalue = master[self.listname] + def format(self): try: - index = listvalue.index(value) + 1 + index = self.listvalue.index(self.value) + 1 except ValueError: - pass - else: - self.value = self.format(index, len(listvalue)) + index = 0 + return '%d/%d' % (index, len(self.listvalue)) + + def update(self, master, width): + self.value = master[self.name] + self.listvalue = master[self.listname] + diff --git a/ttystatus/index_tests.py b/ttystatus/index_tests.py index 9d825b3..3c91903 100644 --- a/ttystatus/index_tests.py +++ b/ttystatus/index_tests.py @@ -33,5 +33,5 @@ class IndexTests(unittest.TestCase): def test_handles_value_not_in_list(self): self.w.update({ 'foo': 'xxx', 'foos': ['a', 'x', 'b'] }, 999) - self.assertEqual(str(self.w), '0/0') + self.assertEqual(str(self.w), '0/3') diff --git a/ttystatus/literal.py b/ttystatus/literal.py index 7d8eb19..5c41b69 100644 --- a/ttystatus/literal.py +++ b/ttystatus/literal.py @@ -23,3 +23,7 @@ class Literal(ttystatus.Widget): def __init__(self, string): self.value = string + self.interesting_keys = [] + + def format(self): + return self.value diff --git a/ttystatus/messager.py b/ttystatus/messager.py index 43fb0e1..c322590 100644 --- a/ttystatus/messager.py +++ b/ttystatus/messager.py @@ -85,14 +85,17 @@ class Messager(object): self._raw_write('\r' + (' ' * len(self._last_msg)) + '\r') self._raw_write(string) self._last_msg = string + + def time_to_write(self): + '''Is it time to write now?''' + return self._now() - self._last_time >= self._period def write(self, string): '''Write raw data, but only once per period.''' string = string[:self.width] - now = self._now() - if now - self._last_time >= self._period: + if self.time_to_write(): self._overwrite(string) - self._last_time = now + self._last_time = self._now() self._cached_msg = string def clear(self): diff --git a/ttystatus/messager_tests.py b/ttystatus/messager_tests.py index 4830727..b57fbaf 100644 --- a/ttystatus/messager_tests.py +++ b/ttystatus/messager_tests.py @@ -46,6 +46,18 @@ class MessagerTests(unittest.TestCase): def test_raw_writes_something_if_output_is_not_a_terminal(self): self.messager._raw_write('foo') self.assertEqual(self.output.getvalue(), 'foo') + + def test_knows_it_is_time_to_write_at_start(self): + self.assert_(self.messager.time_to_write()) + + def test_knows_it_is_not_time_to_write_right_after_previous_one(self): + self.messager._last_time = self.messager._now() + self.assertFalse(self.messager.time_to_write()) + + def test_knows_it_is_time_to_write_after_a_period(self): + self.messager._last_time = (self.messager._now() - + self.messager._period*2) + self.assert_(self.messager.time_to_write()) def test_cached_write_writes_first_thing(self): self.messager.write('foo') diff --git a/ttystatus/pathname.py b/ttystatus/pathname.py index ac51a31..0f5fb43 100644 --- a/ttystatus/pathname.py +++ b/ttystatus/pathname.py @@ -27,13 +27,21 @@ class Pathname(ttystatus.Widget): def __init__(self, key): self._key = key - - def update(self, master, width): - v = master.get(self._key, '') - if len(v) > width: + self.interesting_keys = [key] + self.pathname = '' + self.width = 0 + + def format(self): + v = self.pathname + if len(v) > self.width: ellipsis = '...' - if len(ellipsis) < width: - v = ellipsis + v[-(width - len(ellipsis)):] + if len(ellipsis) < self.width: + v = ellipsis + v[-(self.width - len(ellipsis)):] else: - v = v[-width:] - self.value = v + v = v[-self.width:] + return v + + def update(self, master, width): + self.pathname = master.get(self._key, '') + self.width = width + diff --git a/ttystatus/percent.py b/ttystatus/percent.py index 9750cac..8914f41 100644 --- a/ttystatus/percent.py +++ b/ttystatus/percent.py @@ -25,17 +25,19 @@ class PercentDone(ttystatus.Widget): self.done_name = done_name self.total_name = total_name self.decimals = decimals - self.value = self.format(0, 1) + self.done = 0 + self.total = 1 + self.interesting_keys = [done_name, total_name] - def format(self, done, total): + def format(self): try: - done = float(done) - total = float(total) + done = float(self.done) + total = float(self.total) except ValueError: - return self.value - else: - return '%.*f %%' % (self.decimals, 100.0 * done / total) + done = 0 + total = 1 + return '%.*f %%' % (self.decimals, 100.0 * done / total) def update(self, master, width): - self.value = self.format(master[self.done_name], - master[self.total_name]) + self.done = master[self.done_name] + self.total = master[self.total_name] diff --git a/ttystatus/progressbar.py b/ttystatus/progressbar.py index aca8ea3..e8549f2 100644 --- a/ttystatus/progressbar.py +++ b/ttystatus/progressbar.py @@ -24,15 +24,28 @@ class ProgressBar(ttystatus.Widget): def __init__(self, done_name, total_name): self.done_name = done_name self.total_name = total_name - - def update(self, master, width): - done = float(master.get(self.done_name, 0)) - total = float(master.get(self.total_name, 1) or 0) + self.interesting_keys = [done_name, total_name] + self.done = 0 + self.total = 1 + self.width = 0 + + def format(self): + try: + done = float(self.done) + total = float(self.total) + except ValueError: + done = 0 + total = 1 if total == 0: fraction = 0 else: fraction = done / total - n_stars = int(round(fraction * width)) - n_dashes = int(width - n_stars) - self.value = ('#' * n_stars) + ('-' * n_dashes) + n_stars = int(round(fraction * self.width)) + n_dashes = int(self.width - n_stars) + return ('#' * n_stars) + ('-' * n_dashes) + + def update(self, master, width): + self.done = master[self.done_name] + self.total = master[self.total_name] + self.width = width diff --git a/ttystatus/remtime.py b/ttystatus/remtime.py index b1cffe5..fd0d07c 100644 --- a/ttystatus/remtime.py +++ b/ttystatus/remtime.py @@ -28,7 +28,9 @@ class RemainingTime(ttystatus.Widget): self.total_name = total_name self.started = None self.default = '--h--m--s' - self.value = self.default + self.interesting_keys = [done_name, total_name] + self.done = 0 + self.total = 1 def get_time(self): # pragma: no cover '''Return current time. @@ -40,24 +42,22 @@ class RemainingTime(ttystatus.Widget): return time.time() - def format(self, secs): - hours = secs / (60 * 60) - secs %= (60 * 60) - mins = secs / 60 - secs %= 60 - return '%02dh%02dm%02ds' % (hours, mins, secs) - - def update(self, master, width): + def format(self): if self.started is None: self.started = self.get_time() duration = self.get_time() - self.started if duration >= 1.0: - done = float(master.get(self.done_name, 0) or 0) - total = float(master.get(self.total_name, 0) or 0) - speed = done / duration - remaining = total - done + speed = self.done / duration + remaining = self.total - self.done if speed > 0: secs = remaining / speed - self.value = self.format(secs) - else: - self.value = self.default + hours = secs / (60 * 60) + secs %= (60 * 60) + mins = secs / 60 + secs %= 60 + return '%02dh%02dm%02ds' % (hours, mins, secs) + return self.default + + def update(self, master, width): + self.done = master[self.done_name] + self.total = master[self.total_name] diff --git a/ttystatus/remtime_tests.py b/ttystatus/remtime_tests.py index 6c6f452..2c67345 100644 --- a/ttystatus/remtime_tests.py +++ b/ttystatus/remtime_tests.py @@ -29,6 +29,7 @@ class RemainingTimeTests(unittest.TestCase): self.assertEqual(str(self.w), '--h--m--s') def test_estimates_and_formats_correctly(self): + self.assertEqual(str(self.w), '--h--m--s') self.w.update({ 'done': 0, 'total': 100 }, 999) self.w.get_time = lambda: 5.0 self.w.update({ 'done': 5, 'total': 100 }, 999) diff --git a/ttystatus/status.py b/ttystatus/status.py index f810525..138b615 100644 --- a/ttystatus/status.py +++ b/ttystatus/status.py @@ -37,13 +37,20 @@ class TerminalStatus(object): def add(self, widget): '''Add a new widget to the status display.''' self._widgets.append(widget) + if widget.interesting_keys is None: + self._wildcards += [widget] + else: + for key in widget.interesting_keys: + self._interests[key] = self._interests.get(key, []) + [widget] def clear(self): '''Remove all widgets.''' self._widgets = [] self._values = dict() + self._interests = dict() + self._wildcards = list() self._m.clear() - + def __getitem__(self, key): '''Return value for key, or the empty string.''' return self._values.get(key, '') @@ -56,10 +63,11 @@ class TerminalStatus(object): '''Set value for key.''' self._values[key] = value width = self._m.width - for w in self._widgets: + for w in self._interests.get(key, []) + self._wildcards: w.update(self, width) width -= len(str(w)) - self._m.write(''.join(str(w) for w in self._widgets)) + if self._m.time_to_write(): + self._m.write(''.join(str(w) for w in self._widgets)) def increase(self, key, delta): '''Increase value for a key by a given amount.''' diff --git a/ttystatus/status_tests.py b/ttystatus/status_tests.py index 36bc4bd..36716fd 100644 --- a/ttystatus/status_tests.py +++ b/ttystatus/status_tests.py @@ -25,6 +25,9 @@ class DummyMessager(object): def clear(self): pass + + def time_to_write(self): + return True def write(self, string): pass @@ -49,6 +52,22 @@ class TerminalStatusTests(unittest.TestCase): self.ts.add(w) self.assertEqual(self.ts._widgets, [w]) + def test_adds_widget_as_interested_in_keys(self): + class W(ttystatus.Widget): + def __init__(self): + self.interesting_keys = ['foo'] + w = W() + self.ts.add(w) + self.assert_(w in self.ts._interests['foo']) + + def test_adds_widget_to_wildcards(self): + class W(ttystatus.Widget): + def __init__(self): + self.interesting_keys = None + w = W() + self.ts.add(w) + self.assert_(w in self.ts._wildcards) + def test_removes_all_widgets(self): self.ts.add(ttystatus.Literal('foo')) self.ts.clear() diff --git a/ttystatus/string.py b/ttystatus/string.py index 34b2638..1dc3023 100644 --- a/ttystatus/string.py +++ b/ttystatus/string.py @@ -23,6 +23,11 @@ class String(ttystatus.Widget): def __init__(self, key): self._key = key + self.interesting_keys = [key] + self.value = '' + + def format(self): + return self.value def update(self, master, width): self.value = master[self._key] diff --git a/ttystatus/widget.py b/ttystatus/widget.py index 34d0787..0f03365 100644 --- a/ttystatus/widget.py +++ b/ttystatus/widget.py @@ -23,14 +23,39 @@ class Widget(object): master TerminalStatus widget. They return the formatted string via __str__. + A widget's value may be derived from values stored in the TerminalStatus + widget (called master). Each such value has a key. Computing a widget's + value is a two-step process: when the values associated with keys + are updated, the widget's update() method is called to notify it of + this. update() may compute intermediate values, such as maintain a + counter of the number of changes. It should avoid costly operations + that are only needed when the widget's formatted value is needed. + Those should go into the format() method instead. Thus, update() would + update a counter, format() would create a string representing the + counter. + + This is necessary because actual on-screen updates only happen + every so often, not every time a value in the master changes, and + often the string formatting part is expensive. + + Widgets must have an attribute 'interesting_keys', listing the + keys it is interested in. + ''' def __str__(self): '''Return current value to be displayed for this widget.''' - if hasattr(self, 'value'): - return self.value - else: - return '' + return self.format() + + def format(self): + '''Format the current value. + + This will be called only when the value actually needs to be + formatted. + + ''' + + return '' def update(self, master, width): '''Update displayed value for widget, from values in master. |