summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2010-07-07 09:41:19 +1200
committerLars Wirzenius <liw@liw.fi>2010-07-07 09:41:19 +1200
commit139e35b55938060d80e04750fc36229635fde587 (patch)
treefdd8b7ce20622f8df2d52274659895b8c7937410
parent56f628bb3d49a52c6e5ee1360b520c6381ebd510 (diff)
parente6618ad0ed66ed459a55ecd903a5627dacf97915 (diff)
downloadpython-ttystatus-139e35b55938060d80e04750fc36229635fde587.tar.gz
Merge optimizations.
-rw-r--r--example.py6
-rw-r--r--ttystatus/bytesize.py13
-rw-r--r--ttystatus/counter.py7
-rw-r--r--ttystatus/elapsed.py10
-rw-r--r--ttystatus/index.py23
-rw-r--r--ttystatus/index_tests.py2
-rw-r--r--ttystatus/literal.py4
-rw-r--r--ttystatus/messager.py9
-rw-r--r--ttystatus/messager_tests.py12
-rw-r--r--ttystatus/pathname.py24
-rw-r--r--ttystatus/percent.py20
-rw-r--r--ttystatus/progressbar.py27
-rw-r--r--ttystatus/remtime.py32
-rw-r--r--ttystatus/remtime_tests.py1
-rw-r--r--ttystatus/status.py14
-rw-r--r--ttystatus/status_tests.py19
-rw-r--r--ttystatus/string.py5
-rw-r--r--ttystatus/widget.py33
18 files changed, 183 insertions, 78 deletions
diff --git a/example.py b/example.py
index a1a2c73..3b7b8c8 100644
--- a/example.py
+++ b/example.py
@@ -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.