From: jab Date: Sat, 24 Sep 2016 21:25:31 +0000 (+0000) Subject: Fix and improve do_truncate X-Git-Tag: 2.9~27^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9f9606bc93da879db2071c2112f90437c5ac2eb3;p=thirdparty%2Fjinja.git Fix and improve do_truncate - Ensure that the requested max length be at least as long as the requested `end` value (e.g. 3 in the case of '...'). Fixes #539 - Add `leeway` parameter so that strings that just barely miss the requested max length cutoff can still be spared from unwanted truncation. Default value is 5. - No longer append a space before appending `end` under any circumstances. Adding whitespace before ellipsis punctuation in English is grammatically incorrect. --- diff --git a/jinja2/filters.py b/jinja2/filters.py index a4504069..d4ab46b1 100644 --- a/jinja2/filters.py +++ b/jinja2/filters.py @@ -455,31 +455,35 @@ def do_indent(s, width=4, indentfirst=False): return rv -def do_truncate(s, length=255, killwords=False, end='...'): +def do_truncate(s, length=255, killwords=False, end='...', leeway=5): """Return a truncated copy of the string. The length is specified with the first parameter which defaults to ``255``. If the second parameter is ``true`` the filter will cut the text at length. Otherwise it will discard the last word. If the text was in fact truncated it will append an ellipsis sign (``"..."``). If you want a different ellipsis sign than ``"..."`` you can specify it using the - third parameter. + third parameter. Strings that only exceed the length by the tolerance + margin given in the fourth parameter will not be truncated. .. sourcecode:: jinja - {{ "foo bar baz"|truncate(9) }} - -> "foo ..." - {{ "foo bar baz"|truncate(9, True) }} + {{ "foo bar baz qux"|truncate(9) }} + -> "foo..." + {{ "foo bar baz qux"|truncate(9, True) }} -> "foo ba..." + {{ "foo bar baz qux"|truncate(11) }} + -> "foo bar baz qux" + {{ "foo bar baz qux"|truncate(11, False, '...', 0) }} + -> "foo bar..." """ - if len(s) <= length: + assert length >= len(end), 'expected length >= %s, got %s' % (len(end), length) + assert leeway >= 0, 'expected leeway >= 0, got %s' % leeway + if len(s) <= length + leeway: return s - elif killwords: + if killwords: return s[:length - len(end)] + end - result = s[:length - len(end)].rsplit(' ', 1)[0] - if len(result) < length: - result += ' ' return result + end diff --git a/tests/test_filters.py b/tests/test_filters.py index f8fdad8a..b68bd229 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -239,7 +239,7 @@ class TestFilter(): out = tmpl.render(data='foobar baz bar' * 1000, smalldata='foobar baz bar') msg = 'Current output: %s' % out - assert out == 'foobar baz b>>>|foobar baz >>>|foobar baz bar', msg + assert out == 'foobar baz b>>>|foobar baz>>>|foobar baz bar', msg def test_truncate_very_short(self, env): tmpl = env.from_string( @@ -247,12 +247,12 @@ class TestFilter(): '{{ "foo bar baz"|truncate(9, true) }}' ) out = tmpl.render() - assert out == 'foo ...|foo ba...', out + assert out == 'foo bar baz|foo bar baz', out def test_truncate_end_length(self, env): - tmpl = env.from_string('{{ "Joel is a slug"|truncate(9, true) }}') + tmpl = env.from_string('{{ "Joel is a slug"|truncate(7, true) }}') out = tmpl.render() - assert out == 'Joel i...', 'Current output: %s' % out + assert out == 'Joel...', 'Current output: %s' % out def test_upper(self, env): tmpl = env.from_string('{{ "foo"|upper }}')