From 9b0256e2b3d70b2c4d529cb8888d73a1538000cc Mon Sep 17 00:00:00 2001 From: Vitja Makarov Date: Sat, 24 Aug 2013 00:12:13 +0400 Subject: Fix NullCallableString() when called with no or multiple arguments --- blessings/__init__.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/blessings/__init__.py b/blessings/__init__.py index 276565b..5efcb0b 100644 --- a/blessings/__init__.py +++ b/blessings/__init__.py @@ -453,10 +453,12 @@ class NullCallableString(unicode): new = unicode.__new__(cls, u'') return new - def __call__(self, arg): - if isinstance(arg, int): + def __call__(self, *args): + if len(args) != 1: + return u'' + if isinstance(args[0], int): return u'' - return arg # TODO: Force even strs in Python 2.x to be unicodes? Nah. How would I know what encoding to use to convert it? + return args[0] # TODO: Force even strs in Python 2.x to be unicodes? Nah. How would I know what encoding to use to convert it? def split_into_formatters(compound): -- cgit v1.2.1 From 1e3454aec2e987e17981f0ae268521da3ef50183 Mon Sep 17 00:00:00 2001 From: Vitja Makarov Date: Mon, 26 Aug 2013 08:55:48 +0400 Subject: Add testcase for NullCallableString --- blessings/tests.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/blessings/tests.py b/blessings/tests.py index a885c65..85855d8 100644 --- a/blessings/tests.py +++ b/blessings/tests.py @@ -254,3 +254,12 @@ def test_force_styling_none(): """If ``force_styling=None`` is passed to the constructor, don't ever do styling.""" t = TestTerminal(force_styling=None) eq_(t.save, '') + + +def test_null_callable_string(): + """With no tty NullCallableString is used.""" + t = TestTerminal(stream=StringIO()) + eq_(t.bold('hello, world'), 'hello, world') + eq_(t.clear(), '') + eq_(t.move(1, 2), '') + eq_(t.move_x(1), '') -- cgit v1.2.1 From 7eb5c1854398d95c054f50f95f75a12b9b42a24b Mon Sep 17 00:00:00 2001 From: Erik Rose Date: Fri, 30 Aug 2013 00:16:39 -0400 Subject: Explain the new behavior to myself. Remove 1 test assertion. No non-test behavior changes. Bump version to 1.5.1. Remove one test assertion that already occurs almost verbatim in test_formatting_functions_without_tty(). --- README.rst | 2 ++ blessings/__init__.py | 47 +++++++++++++++++++++++++++++++++++++---------- blessings/tests.py | 8 ++++++-- setup.py | 2 +- 4 files changed, 46 insertions(+), 13 deletions(-) diff --git a/README.rst b/README.rst index f5390fb..1012a17 100644 --- a/README.rst +++ b/README.rst @@ -438,6 +438,8 @@ Version History * Add Travis support. * Make `python setup.py test` work without spurious errors on 2.6. * Work around a tox parsing bug in its config file. + * Parametrizing a capability no longer crashes when there is no tty. (Vitja + Makarov) 1.5 * Add syntactic sugar and documentation for ``enter_fullscreen`` and diff --git a/blessings/__init__.py b/blessings/__init__.py index 5efcb0b..725602e 100644 --- a/blessings/__init__.py +++ b/blessings/__init__.py @@ -152,7 +152,7 @@ class Terminal(object): no_underline='rmul') def __getattr__(self, attr): - """Return parametrized terminal capabilities, like bold. + """Return a terminal capability, like bold. For example, you can say ``term.bold`` to get the string that turns on bold formatting and ``term.normal`` to get the string that turns it off @@ -307,7 +307,11 @@ class Terminal(object): return colors if colors >= 0 else 0 def _resolve_formatter(self, attr): - """Resolve a sugary or plain capability name, color, or compound formatting function name into a callable capability.""" + """Resolve a sugary or plain capability name, color, or compound formatting function name into a callable capability. + + Return a ``ParametrizingString`` or a ``FormattingString``. + + """ if attr in COLORS: return self._resolve_color(attr) elif attr in COMPOUNDABLES: @@ -442,11 +446,9 @@ class FormattingString(unicode): class NullCallableString(unicode): - """A dummy class to stand in for ``FormattingString`` and ``ParametrizingString`` + """A dummy callable Unicode to stand in for ``FormattingString`` and ``ParametrizingString`` - A callable bytestring that returns an empty Unicode when called with an int - and the arg otherwise. We use this when there is no tty and so all - capabilities are blank. + We use this when there is no tty and thus all capabilities should be blank. """ def __new__(cls): @@ -454,11 +456,36 @@ class NullCallableString(unicode): return new def __call__(self, *args): - if len(args) != 1: - return u'' - if isinstance(args[0], int): + """Return a Unicode or whatever you passed in as the first arg (hopefully a string of some kind). + + When called with an int as the first arg, return an empty Unicode. An + int is a good hint that I am a ``ParametrizingString``, as there are + only about half a dozen string-returning capabilities on OS X's + terminfo man page which take any param that's not an int, and those are + seldom if ever used on modern terminal emulators. (Most have to do with + programming function keys. Blessings' story for supporting + non-string-returning caps is undeveloped.) And any parametrized + capability in a situation where all capabilities themselves are taken + to be blank are, of course, themselves blank. + + When called with a non-int as the first arg (no no args at all), return + the first arg. I am acting as a ``FormattingString``. + + """ + if len(args) != 1 or isinstance(args[0], int): + # I am acting as a ParametrizingString. + + # tparm can take not only ints but also (at least) strings as its + # second...nth args. But we don't support callably parametrizing + # caps that take non-ints yet, so we can cheap out here. TODO: Go + # through enough of the motions in the capability resolvers to + # determine which of 2 special-purpose classes, + # NullParametrizableString or NullFormattingString, to return, and + # retire this one. return u'' - return args[0] # TODO: Force even strs in Python 2.x to be unicodes? Nah. How would I know what encoding to use to convert it? + return args[0] # Should we force even strs in Python 2.x to be + # unicodes? No. How would I know what encoding to use + # to convert it? def split_into_formatters(compound): diff --git a/blessings/tests.py b/blessings/tests.py index 85855d8..c7a3527 100644 --- a/blessings/tests.py +++ b/blessings/tests.py @@ -257,9 +257,13 @@ def test_force_styling_none(): def test_null_callable_string(): - """With no tty NullCallableString is used.""" + """Make sure NullCallableString tolerates all numbers and kinds of args it might receive.""" t = TestTerminal(stream=StringIO()) - eq_(t.bold('hello, world'), 'hello, world') + + # I don't promise this will keep working; it's not documented anywhere. + # However, it's what I intend to happen in an edge case, so let's make sure + # it works. eq_(t.clear(), '') + eq_(t.move(1, 2), '') eq_(t.move_x(1), '') diff --git a/setup.py b/setup.py index 7dd18d0..ba705da 100644 --- a/setup.py +++ b/setup.py @@ -16,7 +16,7 @@ if sys.version_info >= (3,): setup( name='blessings', - version='1.5', + version='1.5.1', description='A thin, practical wrapper around terminal coloring, styling, and positioning', long_description=open('README.rst').read(), author='Erik Rose', -- cgit v1.2.1