37
36
return u''.join(_generator())
38
37
wrap = allow_lazy(wrap, unicode)
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)
45
46
if len(words) > length:
46
47
words = words[:length]
47
if not words[-1].endswith('...'):
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)
52
def truncate_html_words(s, num):
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. 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)
78
78
# It's an actual non-HTML word
80
80
if words == length:
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
88
88
closing_tag, tagname, self_closing = tag.groups()
158
160
def phone2numeric(phone):
159
161
"Converts a phone number with letters into its numeric equivalent."
160
letters = re.compile(r'[A-PR-Y]', re.I)
161
char2number = lambda m: {'a': '2', 'c': '2', 'b': '2', 'e': '3',
162
'd': '3', 'g': '4', 'f': '3', 'i': '4', 'h': '4', 'k': '5',
163
'j': '5', 'm': '6', 'l': '5', 'o': '6', 'n': '6', 'p': '7',
164
's': '7', 'r': '7', 'u': '8', 't': '8', 'w': '9', 'v': '8',
165
'y': '9', 'x': '9'}.get(m.group(0).lower())
162
letters = re.compile(r'[A-Z]', re.I)
163
char2number = lambda m: {'a': '2', 'b': '2', 'c': '2', 'd': '3', 'e': '3',
164
'f': '3', 'g': '4', 'h': '4', 'i': '4', 'j': '5', 'k': '5', 'l': '5',
165
'm': '6', 'n': '6', 'o': '6', 'p': '7', 'q': '7', 'r': '7', 's': '7',
166
't': '8', 'u': '8', 'v': '8', 'w': '9', 'x': '9', 'y': '9', 'z': '9',
167
}.get(m.group(0).lower())
166
168
return letters.sub(char2number, phone)
167
169
phone2numeric = allow_lazy(phone2numeric)
200
202
# Expression to match some_token and some_token="with spaces" (and similarly
201
203
# for single-quoted strings).
202
204
smart_split_re = re.compile(r"""
203
([^\s"]*"(?:[^"\\]*(?:\\.[^"\\]*)*)"\S*|
204
[^\s']*'(?:[^'\\]*(?:\\.[^'\\]*)*)'\S*|
208
(?:"(?:[^"\\]|\\.)*" | '(?:[^'\\]|\\.)*')
207
214
def smart_split(text):