Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #6799 - added an `end_text` argument to `truncate_words`/`trunc…

…ate_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
  • Loading branch information...
commit 1d078be448ab45f5e865f50053a23705023e3c53 1 parent 7578981
Jacob Kaplan-Moss authored February 14, 2010
30  django/utils/text.py
... ...
@@ -1,5 +1,4 @@
1 1
 import re
2  
-from django.conf import settings
3 2
 from django.utils.encoding import force_unicode
4 3
 from django.utils.functional import allow_lazy
5 4
 from django.utils.translation import ugettext_lazy
@@ -37,24 +36,25 @@ def _generator():
37 36
     return u''.join(_generator())
38 37
 wrap = allow_lazy(wrap, unicode)
39 38
 
40  
-def truncate_words(s, num):
41  
-    "Truncates a string after a certain number of words."
  39
+def truncate_words(s, num, end_text='...'):
  40
+    """Truncates a string after a certain number of words. Takes an optional
  41
+    argument of what should be used to notify that the string has been
  42
+    truncated, defaults to ellipsis (...)"""
42 43
     s = force_unicode(s)
43 44
     length = int(num)
44 45
     words = s.split()
45 46
     if len(words) > length:
46 47
         words = words[:length]
47  
-        if not words[-1].endswith('...'):
48  
-            words.append('...')
  48
+        if not words[-1].endswith(end_text):
  49
+            words.append(end_text)
49 50
     return u' '.join(words)
50 51
 truncate_words = allow_lazy(truncate_words, unicode)
51 52
 
52  
-def truncate_html_words(s, num):
53  
-    """
54  
-    Truncates html to a certain number of words (not counting tags and
  53
+def truncate_html_words(s, num, end_text='...'):
  54
+    """Truncates html to a certain number of words (not counting tags and
55 55
     comments). Closes opened tags if they were correctly closed in the given
56  
-    html.
57  
-    """
  56
+    html. Takes an optional argument of what should be used to notify that the
  57
+    string has been truncated, defaults to ellipsis (...)."""
58 58
     s = force_unicode(s)
59 59
     length = int(num)
60 60
     if length <= 0:
@@ -65,7 +65,7 @@ def truncate_html_words(s, num):
65 65
     re_tag = re.compile(r'<(/)?([^ ]+?)(?: (/)| .*?)?>')
66 66
     # Count non-HTML words and keep note of open tags
67 67
     pos = 0
68  
-    ellipsis_pos = 0
  68
+    end_text_pos = 0
69 69
     words = 0
70 70
     open_tags = []
71 71
     while words <= length:
@@ -78,11 +78,11 @@ def truncate_html_words(s, num):
78 78
             # It's an actual non-HTML word
79 79
             words += 1
80 80
             if words == length:
81  
-                ellipsis_pos = pos
  81
+                end_text_pos = pos
82 82
             continue
83 83
         # Check for tag
84 84
         tag = re_tag.match(m.group(0))
85  
-        if not tag or ellipsis_pos:
  85
+        if not tag or end_text_pos:
86 86
             # Don't worry about non tags or tags after our truncate point
87 87
             continue
88 88
         closing_tag, tagname, self_closing = tag.groups()
@@ -104,7 +104,9 @@ def truncate_html_words(s, num):
104 104
     if words <= length:
105 105
         # Don't try to close tags if we don't need to truncate
106 106
         return s
107  
-    out = s[:ellipsis_pos] + ' ...'
  107
+    out = s[:end_text_pos]
  108
+    if end_text:
  109
+        out += ' ' + end_text
108 110
     # Close any tags still open
109 111
     for tag in open_tags:
110 112
         out += '</%s>' % tag
20  tests/regressiontests/utils/tests.py
@@ -4,7 +4,7 @@
4 4
 
5 5
 from unittest import TestCase
6 6
 
7  
-from django.utils import html, checksums
  7
+from django.utils import html, checksums, text
8 8
 from django.utils.functional import SimpleLazyObject
9 9
 
10 10
 import timesince
@@ -244,6 +244,24 @@ def test_deepcopy(self):
244 244
         s3 = copy.deepcopy(s)
245 245
         self.assertEqual(s3, complex_object())
246 246
 
  247
+class TestUtilsText(TestCase):
  248
+
  249
+    def test_truncate_words(self):
  250
+        self.assertEqual(u'The quick brown fox jumped over the lazy dog.',
  251
+            text.truncate_words(u'The quick brown fox jumped over the lazy dog.', 10))
  252
+        self.assertEqual(u'The quick brown fox ...',
  253
+            text.truncate_words('The quick brown fox jumped over the lazy dog.', 4))
  254
+        self.assertEqual(u'The quick brown fox ....',
  255
+            text.truncate_words('The quick brown fox jumped over the lazy dog.', 4, '....'))
  256
+        self.assertEqual(u'<p><strong><em>The quick brown fox jumped over the lazy dog.</em></strong></p>',
  257
+            text.truncate_html_words('<p><strong><em>The quick brown fox jumped over the lazy dog.</em></strong></p>', 10))
  258
+        self.assertEqual(u'<p><strong><em>The quick brown fox ...</em></strong></p>',
  259
+            text.truncate_html_words('<p><strong><em>The quick brown fox jumped over the lazy dog.</em></strong></p>', 4))
  260
+        self.assertEqual(u'<p><strong><em>The quick brown fox ....</em></strong></p>',
  261
+            text.truncate_html_words('<p><strong><em>The quick brown fox jumped over the lazy dog.</em></strong></p>', 4, '....'))
  262
+        self.assertEqual(u'<p><strong><em>The quick brown fox</em></strong></p>',
  263
+            text.truncate_html_words('<p><strong><em>The quick brown fox jumped over the lazy dog.</em></strong></p>', 4, None))
  264
+
247 265
 if __name__ == "__main__":
248 266
     import doctest
249 267
     doctest.testmod()

0 notes on commit 1d078be

Please sign in to comment.
Something went wrong with that request. Please try again.