Ticket #393: str_filters.patch
File str_filters.patch, 10.1 KB (added by , 18 years ago) |
---|
-
django/template/__init__.py
574 574 def args_check(name, func, provided): 575 575 provided = list(provided) 576 576 plen = len(provided) 577 # Check to see if a decorator is providing the real function. 578 func = getattr(func, '_decorated_function', func) 577 579 args, varargs, varkw, defaults = getargspec(func) 578 580 # First argument is filter input. 579 581 args.pop(0) -
django/template/defaultfilters.py
8 8 9 9 register = Library() 10 10 11 ####################### 12 # STRING DECORATOR # 13 ####################### 14 15 def smart_string(obj): 16 # FUTURE: Unicode strings should probably be normalized to a specific 17 # encoding and non-unicode strings should be converted to unicode too. 18 # if isinstance(obj, unicode): 19 # obj = obj.encode(settings.DEFAULT_CHARSET) 20 # else: 21 # obj = unicode(obj, settings.DEFAULT_CHARSET) 22 # FUTURE: Replace dumb string logic below with cool unicode logic above. 23 if not isinstance(obj, basestring): 24 obj = str(obj) 25 return obj 26 27 def to_str(func): 28 """ 29 Decorator for filters which should only receive strings. The object passed 30 as the first positional argument will be converted to a string. 31 """ 32 def _dec(*args, **kwargs): 33 if args: 34 args = list(args) 35 args[0] = smart_string(args[0]) 36 return func(*args, **kwargs) 37 # Make sure the internal name is the original function name because this 38 # is the internal name of the filter if passed directly to Library().filter 39 _dec.__name__ = func.__name__ 40 # Include a reference to the real function (used to check original 41 # arguments by the template parser). 42 _dec._decorated_function = getattr(func, '_decorated_function', func) 43 return _dec 44 11 45 ################### 12 46 # STRINGS # 13 47 ################### 14 48 15 49 50 #@to_str 16 51 def addslashes(value): 17 52 "Adds slashes - useful for passing strings to JavaScript, for example." 18 53 return value.replace('\\', '\\\\').replace('"', '\\"').replace("'", "\\'") 54 addslashes = to_str(addslashes) 19 55 56 #@to_str 20 57 def capfirst(value): 21 58 "Capitalizes the first character of the value" 22 value = str(value)23 59 return value and value[0].upper() + value[1:] 24 60 capfirst = to_str(capfirst) 61 62 #@to_str 25 63 def fix_ampersands(value): 26 64 "Replaces ampersands with ``&`` entities" 27 65 from django.utils.html import fix_ampersands 28 66 return fix_ampersands(value) 67 fix_ampersands = to_str(fix_ampersands) 29 68 30 69 def floatformat(text, arg=-1): 31 70 """ … … 52 91 try: 53 92 d = int(arg) 54 93 except ValueError: 55 return s tr(f)94 return smart_string(f) 56 95 m = f - int(f) 57 96 if not m and d < 0: 58 97 return '%d' % int(f) … … 60 99 formatstr = '%%.%df' % abs(d) 61 100 return formatstr % f 62 101 102 #@to_str 63 103 def linenumbers(value): 64 104 "Displays text with line numbers" 65 105 from django.utils.html import escape … … 69 109 for i, line in enumerate(lines): 70 110 lines[i] = ("%0" + width + "d. %s") % (i + 1, escape(line)) 71 111 return '\n'.join(lines) 112 linenumbers = to_str(linenumbers) 72 113 114 #@to_str 73 115 def lower(value): 74 116 "Converts a string into all lowercase" 75 117 return value.lower() 118 lower = to_str(lower) 76 119 120 #@to_str 77 121 def make_list(value): 78 122 """ 79 123 Returns the value turned into a list. For an integer, it's a list of 80 124 digits. For a string, it's a list of characters. 81 125 """ 82 return list(str(value)) 126 return list(value) 127 make_list = to_str(make_list) 83 128 129 #@to_str 84 130 def slugify(value): 85 131 "Converts to lowercase, removes non-alpha chars and converts spaces to hyphens" 86 132 value = re.sub('[^\w\s-]', '', value).strip().lower() 87 133 return re.sub('[-\s]+', '-', value) 134 slugify = to_str(slugify) 88 135 89 136 def stringformat(value, arg): 90 137 """ … … 96 143 of Python string formatting 97 144 """ 98 145 try: 99 return ("%" + arg) % value146 return ("%" + str(arg)) % value 100 147 except (ValueError, TypeError): 101 148 return "" 102 149 150 #@to_str 103 151 def title(value): 104 152 "Converts a string into titlecase" 105 153 return re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title()) 154 title = to_str(title) 106 155 156 #@to_str 107 157 def truncatewords(value, arg): 108 158 """ 109 159 Truncates a string after a certain number of words … … 115 165 length = int(arg) 116 166 except ValueError: # invalid literal for int() 117 167 return value # Fail silently. 118 if not isinstance(value, basestring):119 value = str(value)120 168 return truncate_words(value, length) 169 truncatewords = to_str(truncatewords) 121 170 171 #@to_str 122 172 def upper(value): 123 173 "Converts a string into all uppercase" 124 174 return value.upper() 175 upper = to_str(upper) 125 176 177 #@to_str 126 178 def urlencode(value): 127 179 "Escapes a value for use in a URL" 128 180 import urllib 129 181 return urllib.quote(value) 182 urlencode = to_str(urlencode) 130 183 184 #@to_str 131 185 def urlize(value): 132 186 "Converts URLs in plain text into clickable links" 133 187 from django.utils.html import urlize 134 188 return urlize(value, nofollow=True) 189 urlize = to_str(urlize) 135 190 191 #@to_str 136 192 def urlizetrunc(value, limit): 137 193 """ 138 194 Converts URLs into clickable links, truncating URLs to the given character limit, … … 142 198 """ 143 199 from django.utils.html import urlize 144 200 return urlize(value, trim_url_limit=int(limit), nofollow=True) 201 urlizetrunc = to_str(urlizetrunc) 145 202 203 #@to_str 146 204 def wordcount(value): 147 205 "Returns the number of words" 148 206 return len(value.split()) 207 wordcount = to_str(wordcount) 149 208 209 #@to_str 150 210 def wordwrap(value, arg): 151 211 """ 152 212 Wraps words at specified line length … … 154 214 Argument: number of characters to wrap the text at. 155 215 """ 156 216 from django.utils.text import wrap 157 return wrap(str(value), int(arg)) 217 return wrap(value, int(arg)) 218 wordwrap = to_str(wordwrap) 158 219 220 #@to_str 159 221 def ljust(value, arg): 160 222 """ 161 223 Left-aligns the value in a field of a given width 162 224 163 225 Argument: field size 164 226 """ 165 return str(value).ljust(int(arg)) 227 return value.ljust(int(arg)) 228 ljust = to_str(ljust) 166 229 230 #@to_str 167 231 def rjust(value, arg): 168 232 """ 169 233 Right-aligns the value in a field of a given width 170 234 171 235 Argument: field size 172 236 """ 173 return str(value).rjust(int(arg)) 237 return value.rjust(int(arg)) 238 rjust = to_str(rjust) 174 239 240 #@to_str 175 241 def center(value, arg): 176 242 "Centers the value in a field of a given width" 177 return str(value).center(int(arg)) 243 return value.center(int(arg)) 244 center = to_str(center) 178 245 246 #@to_str 179 247 def cut(value, arg): 180 248 "Removes all values of arg from the given string" 181 249 return value.replace(arg, '') 250 cut = to_str(cut) 182 251 183 252 ################### 184 253 # HTML STRINGS # 185 254 ################### 186 255 256 #@to_str 187 257 def escape(value): 188 258 "Escapes a string's HTML" 189 259 from django.utils.html import escape 190 260 return escape(value) 261 escape = to_str(escape) 191 262 263 #@to_str 192 264 def linebreaks(value): 193 265 "Converts newlines into <p> and <br />s" 194 266 from django.utils.html import linebreaks 195 267 return linebreaks(value) 268 linebreaks = to_str(linebreaks) 196 269 270 #@to_str 197 271 def linebreaksbr(value): 198 272 "Converts newlines into <br />s" 199 273 return value.replace('\n', '<br />') 274 linebreaksbr = to_str(linebreaksbr) 200 275 276 #@to_str 201 277 def removetags(value, tags): 202 278 "Removes a space separated list of [X]HTML tags from the output" 203 279 tags = [re.escape(tag) for tag in tags.split()] … … 207 283 value = starttag_re.sub('', value) 208 284 value = endtag_re.sub('', value) 209 285 return value 286 removetags = to_str(removetags) 210 287 288 #@to_str 211 289 def striptags(value): 212 290 "Strips all [X]HTML tags" 213 291 from django.utils.html import strip_tags 214 if not isinstance(value, basestring):215 value = str(value)216 292 return strip_tags(value) 293 striptags = to_str(striptags) 217 294 218 295 ################### 219 296 # LISTS # … … 248 325 def join(value, arg): 249 326 "Joins a list with a string, like Python's ``str.join(list)``" 250 327 try: 251 return arg.join(map(s tr, value))328 return arg.join(map(smart_string, value)) 252 329 except AttributeError: # fail silently but nicely 253 330 return value 254 331 -
docs/templates_python.txt
654 654 If you leave off the ``name`` argument, as in the second example above, Django 655 655 will use the function's name as the filter name. 656 656 657 Template filters which expect strings 658 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 659 If you are writing a template filter which only expects a string as the first 660 argument, you should use the included decorator ``to_str`` which will convert 661 an object to it's string value before being passed to your function:: 662 663 def lower(value): 664 return value.lower() 665 lower = template.to_str(lower) 666 657 667 Writing custom template tags 658 668 ---------------------------- 659 669 -
tests/regressiontests/defaultfilters/tests.py
372 372 >>> phone2numeric('0800 flowers') 373 373 '0800 3569377' 374 374 375 # Filters shouldn't break if passed non-strings 376 >>> addslashes(123) 377 '123' 378 >>> linenumbers(123) 379 '1. 123' 380 >>> lower(123) 381 '123' 382 >>> make_list(123) 383 ['1', '2', '3'] 384 >>> slugify(123) 385 '123' 386 >>> title(123) 387 '123' 388 >>> truncatewords(123, 2) 389 '123' 390 >>> upper(123) 391 '123' 392 >>> urlencode(123) 393 '123' 394 >>> urlize(123) 395 '123' 396 >>> urlizetrunc(123, 1) 397 '123' 398 >>> wordcount(123) 399 1 400 >>> wordwrap(123, 2) 401 '123' 402 >>> ljust('123', 4) 403 '123 ' 404 >>> rjust('123', 4) 405 ' 123' 406 >>> center('123', 5) 407 ' 123 ' 408 >>> center('123', 6) 409 ' 123 ' 410 >>> cut(123, '2') 411 '13' 412 >>> escape(123) 413 '123' 414 >>> linebreaks(123) 415 '<p>123</p>' 416 >>> linebreaksbr(123) 417 '123' 418 >>> removetags(123, 'a') 419 '123' 420 >>> striptags(123) 421 '123' 375 422 376 377 423 """ 378 424 379 425 from django.template.defaultfilters import *