From 1d078be448ab45f5e865f50053a23705023e3c53 Mon Sep 17 00:00:00 2001 From: Jacob Kaplan-Moss Date: Sun, 14 Feb 2010 18:36:48 +0000 Subject: [PATCH] Fixed #6799 - added an `end_text` argument to `truncate_words`/`truncate_html_words`. This allows customizing the standard "..." end text. Yes, this is technically a feature sneaking in after the deadline, but I just couldn't bring myself to punt it again: we already used that excuse for not getting it into 1.1. Thanks to Adam Fast and Travis Cline for work on this patch. git-svn-id: http://code.djangoproject.com/svn/django/trunk@12431 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/utils/text.py | 30 +++++++++++++++------------- tests/regressiontests/utils/tests.py | 20 ++++++++++++++++++- 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/django/utils/text.py b/django/utils/text.py index f4163ac135..624f5b6a55 100644 --- a/django/utils/text.py +++ b/django/utils/text.py @@ -1,5 +1,4 @@ import re -from django.conf import settings from django.utils.encoding import force_unicode from django.utils.functional import allow_lazy from django.utils.translation import ugettext_lazy @@ -37,24 +36,25 @@ def wrap(text, width): return u''.join(_generator()) wrap = allow_lazy(wrap, unicode) -def truncate_words(s, num): - "Truncates a string after a certain number of words." +def truncate_words(s, num, end_text='...'): + """Truncates a string after a certain number of words. Takes an optional + argument of what should be used to notify that the string has been + truncated, defaults to ellipsis (...)""" s = force_unicode(s) length = int(num) words = s.split() if len(words) > length: words = words[:length] - if not words[-1].endswith('...'): - words.append('...') + if not words[-1].endswith(end_text): + words.append(end_text) return u' '.join(words) truncate_words = allow_lazy(truncate_words, unicode) -def truncate_html_words(s, num): - """ - Truncates html to a certain number of words (not counting tags and +def truncate_html_words(s, num, end_text='...'): + """Truncates html to a certain number of words (not counting tags and comments). Closes opened tags if they were correctly closed in the given - html. - """ + html. Takes an optional argument of what should be used to notify that the + string has been truncated, defaults to ellipsis (...).""" s = force_unicode(s) length = int(num) if length <= 0: @@ -65,7 +65,7 @@ def truncate_html_words(s, num): re_tag = re.compile(r'<(/)?([^ ]+?)(?: (/)| .*?)?>') # Count non-HTML words and keep note of open tags pos = 0 - ellipsis_pos = 0 + end_text_pos = 0 words = 0 open_tags = [] while words <= length: @@ -78,11 +78,11 @@ def truncate_html_words(s, num): # It's an actual non-HTML word words += 1 if words == length: - ellipsis_pos = pos + end_text_pos = pos continue # Check for tag tag = re_tag.match(m.group(0)) - if not tag or ellipsis_pos: + if not tag or end_text_pos: # Don't worry about non tags or tags after our truncate point continue closing_tag, tagname, self_closing = tag.groups() @@ -104,7 +104,9 @@ def truncate_html_words(s, num): if words <= length: # Don't try to close tags if we don't need to truncate return s - out = s[:ellipsis_pos] + ' ...' + out = s[:end_text_pos] + if end_text: + out += ' ' + end_text # Close any tags still open for tag in open_tags: out += '' % tag diff --git a/tests/regressiontests/utils/tests.py b/tests/regressiontests/utils/tests.py index 96b7b4d298..4ad4e63e33 100644 --- a/tests/regressiontests/utils/tests.py +++ b/tests/regressiontests/utils/tests.py @@ -4,7 +4,7 @@ Tests for django.utils. from unittest import TestCase -from django.utils import html, checksums +from django.utils import html, checksums, text from django.utils.functional import SimpleLazyObject import timesince @@ -244,6 +244,24 @@ class TestUtilsSimpleLazyObject(TestCase): s3 = copy.deepcopy(s) self.assertEqual(s3, complex_object()) +class TestUtilsText(TestCase): + + def test_truncate_words(self): + self.assertEqual(u'The quick brown fox jumped over the lazy dog.', + text.truncate_words(u'The quick brown fox jumped over the lazy dog.', 10)) + self.assertEqual(u'The quick brown fox ...', + text.truncate_words('The quick brown fox jumped over the lazy dog.', 4)) + self.assertEqual(u'The quick brown fox ....', + text.truncate_words('The quick brown fox jumped over the lazy dog.', 4, '....')) + self.assertEqual(u'

The quick brown fox jumped over the lazy dog.

', + text.truncate_html_words('

The quick brown fox jumped over the lazy dog.

', 10)) + self.assertEqual(u'

The quick brown fox ...

', + text.truncate_html_words('

The quick brown fox jumped over the lazy dog.

', 4)) + self.assertEqual(u'

The quick brown fox ....

', + text.truncate_html_words('

The quick brown fox jumped over the lazy dog.

', 4, '....')) + self.assertEqual(u'

The quick brown fox

', + text.truncate_html_words('

The quick brown fox jumped over the lazy dog.

', 4, None)) + if __name__ == "__main__": import doctest doctest.testmod()