summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2011-08-31 16:23:57 +0100
committerLars Wirzenius <liw@liw.fi>2011-08-31 16:23:57 +0100
commit13b08556b2ed9533fc78ead7747af3c4297e8fcb (patch)
tree733933243d5ed09a9f6b957b71e9f5379135ca55
parent31cb8821d1aa08bce246864554f18accd8873774 (diff)
parent0d93553e914d8aeb7eaf5d903f2d082ee7ec5a83 (diff)
downloadpython-ttystatus-13b08556b2ed9533fc78ead7747af3c4297e8fcb.tar.gz
Add format string support.
-rw-r--r--NEWS2
-rw-r--r--doc/index.rst7
-rw-r--r--example.py7
-rw-r--r--ttystatus/__init__.py2
-rw-r--r--ttystatus/fmt.py66
-rw-r--r--ttystatus/fmt_tests.py71
-rw-r--r--ttystatus/status.py12
-rw-r--r--ttystatus/status_tests.py6
8 files changed, 163 insertions, 10 deletions
diff --git a/NEWS b/NEWS
index 9b556c2..3f56359 100644
--- a/NEWS
+++ b/NEWS
@@ -8,6 +8,8 @@ Version 0.14, released XXXXXXXXXX
* A method for printing error messages is provided. They are printed to
the standard error.
* The `PercentDone` class handles zero total amounts better now.
+* Widgets may now be added using a simple format string. See
+ `ttystatus.TerminalStatus.format` method.
Version 0.13, released 2011-08-18
---------------------------------
diff --git a/doc/index.rst b/doc/index.rst
index 5f671bf..7119d5d 100644
--- a/doc/index.rst
+++ b/doc/index.rst
@@ -25,11 +25,8 @@ Here's an example program that searches for symlinks in a directory tree::
import ttystatus
ts = ttystatus.TerminalStatus(period=0.1)
- 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'))
+ ts.format('%ElapsedTime() Looking for files: %Counter(pathname) found, '
+ 'currently in %Pathname(dirname)')
pathnames = []
for dirname, subdirs, basenames in os.walk(sys.argv[1]):
diff --git a/example.py b/example.py
index 909ce46..5814fbc 100644
--- a/example.py
+++ b/example.py
@@ -25,11 +25,8 @@ import ttystatus
def main():
ts = ttystatus.TerminalStatus(period=0.1)
- 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'))
+ ts.format('%ElapsedTime() Looking for files: %Counter(pathname) found, '
+ 'currently in %Pathname(dirname)')
pathnames = []
for dirname, subdirs, basenames in os.walk(sys.argv[1]):
diff --git a/ttystatus/__init__.py b/ttystatus/__init__.py
index 794f0ee..c2cfebc 100644
--- a/ttystatus/__init__.py
+++ b/ttystatus/__init__.py
@@ -33,4 +33,6 @@ from remtime import RemainingTime
from elapsed import ElapsedTime
from bytespeed import ByteSpeed
+import fmt
+
__all__ = locals()
diff --git a/ttystatus/fmt.py b/ttystatus/fmt.py
new file mode 100644
index 0000000..bc0bbe8
--- /dev/null
+++ b/ttystatus/fmt.py
@@ -0,0 +1,66 @@
+# Copyright 2011 Lars Wirzenius
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+import inspect
+import re
+
+import ttystatus
+
+
+def _find_widgets():
+ names = dir(ttystatus)
+ objs = [getattr(ttystatus, x) for x in names]
+ classes = [o for o in objs if inspect.isclass(o)]
+ widgets = [c for c in classes if issubclass(c, ttystatus.Widget)]
+ subclasses = [w for w in widgets if w != ttystatus.Widget]
+ return subclasses
+
+widgets = _find_widgets()
+
+
+def parse(fmt):
+ '''Parse format string.'''
+
+ names = [x.__name__ for x in widgets]
+ namespat = '|'.join(names)
+ argspat = r'[0-9a-zA-Z,_-]*'
+ pat = r'%%(?P<class>%s)\((?P<args>%s)\)' % (namespat, argspat)
+ pat = re.compile(pat)
+
+ result = []
+ prefix = ''
+ while fmt:
+ m = pat.match(fmt)
+ if m:
+ if prefix:
+ result.append(ttystatus.Literal(prefix))
+ prefix = ''
+ klass = getattr(ttystatus, m.group('class'))
+ argnames = m.group('args').split(',')
+ argnames = [x for x in argnames if x]
+ result.append(klass(*argnames))
+ fmt = fmt[m.end():]
+ elif fmt.startswith('%%'):
+ prefix += '%'
+ fmt = fmt[2:]
+ else:
+ prefix += fmt[0]
+ fmt = fmt[1:]
+
+ if prefix:
+ result.append(ttystatus.Literal(prefix))
+ return result
+
diff --git a/ttystatus/fmt_tests.py b/ttystatus/fmt_tests.py
new file mode 100644
index 0000000..af39948
--- /dev/null
+++ b/ttystatus/fmt_tests.py
@@ -0,0 +1,71 @@
+# Copyright 2011 Lars Wirzenius
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+import unittest
+
+import ttystatus
+
+
+class FormatTests(unittest.TestCase):
+
+ def test_knows_widgets(self):
+ self.assertEqual(type(ttystatus.fmt.widgets), list)
+ self.assert_(len(ttystatus.fmt.widgets) > 0)
+ for widget in ttystatus.fmt.widgets:
+ self.assert_(issubclass(widget, ttystatus.Widget))
+ self.assertNotEqual(widget, ttystatus.Widget)
+
+ def test_parses_string_without_widgets(self):
+ x = ttystatus.fmt.parse('hello, world')
+ self.assertEqual(len(x), 1)
+ self.assertEqual(type(x[0]), ttystatus.Literal)
+ self.assertEqual(str(x[0]), 'hello, world')
+
+ def test_parses_escaped_pecent(self):
+ x = ttystatus.fmt.parse('%%')
+ self.assertEqual(len(x), 1)
+ self.assertEqual(type(x[0]), ttystatus.Literal)
+ self.assertEqual(str(x[0]), '%')
+
+ def test_parses_parameterless_widget(self):
+ x = ttystatus.fmt.parse('%ElapsedTime()')
+
+ self.assertEqual(len(x), 1)
+ self.assertEqual(type(x[0]), ttystatus.ElapsedTime)
+
+ def test_parses_widget_with_one_parameter(self):
+ x = ttystatus.fmt.parse('%String(name)')
+
+ self.assertEqual(len(x), 1)
+
+ self.assertEqual(type(x[0]), ttystatus.String)
+ self.assertEqual(x[0]._key, 'name')
+
+ def test_parses_some_widgets(self):
+ x = ttystatus.fmt.parse('hello, %String(name): %ElapsedTime()')
+
+ self.assertEqual(len(x), 4)
+
+ self.assertEqual(type(x[0]), ttystatus.Literal)
+ self.assertEqual(str(x[0]), 'hello, ')
+
+ self.assertEqual(type(x[1]), ttystatus.String)
+
+ self.assertEqual(type(x[2]), ttystatus.Literal)
+ self.assertEqual(str(x[2]), ': ')
+
+ self.assertEqual(type(x[3]), ttystatus.ElapsedTime)
+
diff --git a/ttystatus/status.py b/ttystatus/status.py
index b5d743a..24f369b 100644
--- a/ttystatus/status.py
+++ b/ttystatus/status.py
@@ -44,6 +44,18 @@ class TerminalStatus(object):
else:
for key in widget.interesting_keys:
self._interests[key] = self._interests.get(key, []) + [widget]
+
+ def format(self, format_string):
+ '''Add new widgets based on format string.
+
+ The format string is taken literally, except that ``%%`` is a
+ literal percent character, and ``%Foo(a,b,c)`` is a widget
+ of type ``Foo`` with parameters a, b, and c. For example:
+ ``format("hello, %String(name)")``.
+
+ '''
+ for widget in ttystatus.fmt.parse(format_string):
+ self.add(widget)
def clear(self):
'''Remove all widgets.'''
diff --git a/ttystatus/status_tests.py b/ttystatus/status_tests.py
index 9e6d0ac..3583028 100644
--- a/ttystatus/status_tests.py
+++ b/ttystatus/status_tests.py
@@ -78,6 +78,12 @@ class TerminalStatusTests(unittest.TestCase):
w = W()
self.ts.add(w)
self.assert_(w in self.ts._wildcards)
+
+ def test_adds_widgets_from_format_string(self):
+ self.ts.format('hello, %String(name)')
+ self.assertEqual(len(self.ts._widgets), 2)
+ self.assertEqual(type(self.ts._widgets[0]), ttystatus.Literal)
+ self.assertEqual(type(self.ts._widgets[1]), ttystatus.String)
def test_removes_all_widgets(self):
self.ts.add(ttystatus.Literal('foo'))