Ticket #2359: unicode-autoescape-1.diff
File unicode-autoescape-1.diff, 73.6 KB (added by , 17 years ago) |
---|
-
django/oldforms/__init__.py
From nobody Mon Sep 17 00:00:00 2001 From: Michael Radziej <mir@noris.de> Date: Fri May 25 14:32:37 2007 +0200 Subject: [PATCH] autoescape 1 Refreshed patch autoescape-1. (Base: 1385b65efac6593b1152308f341a15016f6b29d3) (Last: b573a42a7e03c1e50b48d9e8d688db49e8d56f82) --- django/oldforms/__init__.py | 43 ++-- django/template/__init__.py | 26 ++ django/template/context.py | 4 django/template/defaultfilters.py | 131 ++++++++++-- django/template/defaulttags.py | 45 ++++ django/utils/encoding.py | 2 django/utils/html.py | 16 + django/utils/safestring.py | 126 ++++++++++++ docs/templates.txt | 118 +++++++++++ docs/templates_python.txt | 116 +++++++++++ tests/regressiontests/autoescape/__init__.py | 0 tests/regressiontests/autoescape/models.py | 0 tests/regressiontests/autoescape/tests.py | 270 +++++++++++++++++++++++++ tests/regressiontests/defaultfilters/tests.py | 4 tests/regressiontests/templates/tests.py | 144 +++++++------ 15 files changed, 925 insertions(+), 120 deletions(-) create mode 100644 django/utils/safestring.py create mode 100644 tests/regressiontests/autoescape/__init__.py create mode 100644 tests/regressiontests/autoescape/models.py create mode 100644 tests/regressiontests/autoescape/tests.py base fb9e634ca37d3deb02ab7185a7d85ad0398d8356 last 4fc620a019aa2eb53633490b4a883fb2c8577890 diff --git a/django/oldforms/__init__.py b/django/oldforms/__init__.py index bc787f860cd71b006b1f3619839c7d4e22e18587..1999b2041e13688eb52a5365d7c9215e3812974f 100644
a b 1 1 from django.core import validators 2 2 from django.core.exceptions import PermissionDenied 3 3 from django.utils.html import escape 4 from django.utils.safestring import mark_safe 4 5 from django.conf import settings 5 6 from django.utils.translation import ugettext, ungettext 6 7 from django.utils.encoding import smart_unicode, smart_str … … class FormFieldWrapper(object): 188 189 189 190 def html_error_list(self): 190 191 if self.errors(): 191 return '<ul class="errorlist"><li>%s</li></ul>' % '</li><li>'.join([escape(e) for e in self.errors()])192 return mark_safe('<ul class="errorlist"><li>%s</li></ul>' % '</li><li>'.join([escape(e) for e in self.errors()])) 192 193 else: 193 return ''194 return mark_safe('') 194 195 195 196 def get_id(self): 196 197 return self.formfield.get_id() … … class FormFieldCollection(FormFieldWrapp 225 226 return bool(len(self.errors())) 226 227 227 228 def html_combined_error_list(self): 228 return ''.join([field.html_error_list() for field in self.formfield_dict.values() if hasattr(field, 'errors')])229 return mark_safe(''.join([field.html_error_list() for field in self.formfield_dict.values() if hasattr(field, 'errors')])) 229 230 230 231 class InlineObjectCollection(object): 231 232 "An object that acts like a sparse list of form field collections." … … class TextField(FormField): 414 415 maxlength = u'' 415 416 if self.maxlength: 416 417 maxlength = u'maxlength="%s" ' % self.maxlength 417 return u'<input type="%s" id="%s" class="v%s%s" name="%s" size="%s" value="%s" %s/>' % \418 return mark_safe(u'<input type="%s" id="%s" class="v%s%s" name="%s" size="%s" value="%s" %s/>' % \ 418 419 (self.input_type, self.get_id(), self.__class__.__name__, self.is_required and u' required' or '', 419 self.field_name, self.length, escape(data), maxlength) 420 self.field_name, self.length, escape(data), maxlength)) 420 421 421 422 def html2python(data): 422 423 return data … … class LargeTextField(TextField): 438 439 def render(self, data): 439 440 if data is None: 440 441 data = '' 441 return u'<textarea id="%s" class="v%s%s" name="%s" rows="%s" cols="%s">%s</textarea>' % \442 return mark_safe(u'<textarea id="%s" class="v%s%s" name="%s" rows="%s" cols="%s">%s</textarea>' % \ 442 443 (self.get_id(), self.__class__.__name__, self.is_required and u' required' or u'', 443 self.field_name, self.rows, self.cols, escape(data)) 444 self.field_name, self.rows, self.cols, escape(data))) 444 445 445 446 class HiddenField(FormField): 446 447 def __init__(self, field_name, is_required=False, validator_list=None): … … class HiddenField(FormField): 449 450 self.validator_list = validator_list[:] 450 451 451 452 def render(self, data): 452 return u'<input type="hidden" id="%s" name="%s" value="%s" />' % \453 (self.get_id(), self.field_name, escape(data)) 453 return mark_safe(u'<input type="hidden" id="%s" name="%s" value="%s" />' % \ 454 (self.get_id(), self.field_name, escape(data))) 454 455 455 456 class CheckboxField(FormField): 456 457 def __init__(self, field_name, checked_by_default=False, validator_list=None, is_required=False): … … class CheckboxField(FormField): 464 465 checked_html = '' 465 466 if data or (data is '' and self.checked_by_default): 466 467 checked_html = ' checked="checked"' 467 return u'<input type="checkbox" id="%s" class="v%s" name="%s"%s />' % \468 return mark_safe(u'<input type="checkbox" id="%s" class="v%s" name="%s"%s />' % \ 468 469 (self.get_id(), self.__class__.__name__, 469 self.field_name, checked_html) 470 self.field_name, checked_html)) 470 471 471 472 def html2python(data): 472 473 "Convert value from browser ('on' or '') to a Python boolean" … … class SelectField(FormField): 498 499 selected_html = u' selected="selected"' 499 500 output.append(u' <option value="%s"%s>%s</option>' % (escape(value), selected_html, escape(display_name))) 500 501 output.append(u' </select>') 501 return u'\n'.join(output)502 return mark_safe(u'\n'.join(output)) 502 503 503 504 def isValidChoice(self, data, form): 504 505 str_data = smart_unicode(data) … … class RadioSelectField(FormField): 552 553 output = [u'<ul%s>' % (self.ul_class and u' class="%s"' % self.ul_class or u'')] 553 554 output.extend([u'<li>%s %s</li>' % (d['field'], d['label']) for d in self.datalist]) 554 555 output.append(u'</ul>') 555 return u''.join(output)556 return mark_safe(u''.join(output)) 556 557 def __iter__(self): 557 558 for d in self.datalist: 558 559 yield d … … class RadioSelectField(FormField): 567 568 datalist.append({ 568 569 'value': value, 569 570 'name': display_name, 570 'field': u'<input type="radio" id="%s" name="%s" value="%s"%s/>' % \571 (self.get_id() + u'_' + unicode(i), self.field_name, value, selected_html) ,572 'label': u'<label for="%s">%s</label>' % \571 'field': mark_safe(u'<input type="radio" id="%s" name="%s" value="%s"%s/>' % \ 572 (self.get_id() + u'_' + unicode(i), self.field_name, value, selected_html)), 573 'label': mark_safe(u'<label for="%s">%s</label>' % \ 573 574 (self.get_id() + u'_' + unicode(i), display_name), 574 })575 )}) 575 576 return RadioFieldRenderer(datalist, self.ul_class) 576 577 577 578 def isValidChoice(self, data, form): … … class SelectMultipleField(SelectField): 610 611 selected_html = u' selected="selected"' 611 612 output.append(u' <option value="%s"%s>%s</option>' % (escape(value), selected_html, escape(choice))) 612 613 output.append(u' </select>') 613 return u'\n'.join(output)614 return mark_safe(u'\n'.join(output)) 614 615 615 616 def isValidChoice(self, field_data, all_data): 616 617 # data is something like ['1', '2', '3'] … … class CheckboxSelectMultipleField(Select 663 664 (self.get_id() + escape(value), self.__class__.__name__, field_name, checked_html, 664 665 self.get_id() + escape(value), choice)) 665 666 output.append(u'</ul>') 666 return u'\n'.join(output)667 return mark_safe(u'\n'.join(output)) 667 668 668 669 #################### 669 670 # FILE UPLOADS # … … class FileUploadField(FormField): 684 685 raise validators.CriticalValidationError, ugettext("The submitted file is empty.") 685 686 686 687 def render(self, data): 687 return u'<input type="file" id="%s" class="v%s" name="%s" />' % \688 (self.get_id(), self.__class__.__name__, self.field_name) 688 return mark_safe(u'<input type="file" id="%s" class="v%s" name="%s" />' % \ 689 (self.get_id(), self.__class__.__name__, self.field_name)) 689 690 690 691 def html2python(data): 691 692 if data is None: -
django/template/__init__.py
diff --git a/django/template/__init__.py b/django/template/__init__.py index 5b8cdc3b1f1950efa082b098cd351b283bd22834..3f3c611b2ab1909f2357351349c6f89757dfab0a 100644
a b from django.utils.functional import curr 62 62 from django.utils.text import smart_split 63 63 from django.utils.encoding import smart_unicode, smart_str, force_unicode 64 64 from django.utils.translation import ugettext as _ 65 from django.utils.safestring import SafeData, EscapeData, mark_safe, mark_for_escaping 66 from django.utils.html import escape 65 67 66 68 __all__ = ('Template', 'Context', 'RequestContext', 'compile_string') 67 69 … … class FilterExpression(object): 603 605 arg_vals.append(arg) 604 606 else: 605 607 arg_vals.append(resolve_variable(arg, context)) 606 obj = func(obj, *arg_vals) 608 if getattr(func, 'needs_autoescape', False): 609 new_obj = func(obj, autoescape = context.autoescape, *arg_vals) 610 else: 611 new_obj = func(obj, *arg_vals) 612 if getattr(func, 'is_safe', False) and isinstance(obj, SafeData): 613 obj = mark_safe(new_obj) 614 elif isinstance(obj, EscapeData): 615 obj = mark_for_escaping(new_obj) 616 else: 617 obj = new_obj 618 607 619 return obj 608 620 609 621 def args_check(name, func, provided): … … class VariableNode(Node): 792 804 return "<Variable Node: %s>" % self.filter_expression 793 805 794 806 def render(self, context): 795 return self.filter_expression.resolve(context) 807 output = self.filter_expression.resolve(context) 808 if (context.autoescape and not isinstance(output, SafeData)) or isinstance(output, EscapeData): 809 return escape(output) 810 else: 811 return output 796 812 797 813 class DebugVariableNode(VariableNode): 798 814 def render(self, context): 799 815 try: 800 returnself.filter_expression.resolve(context)816 output = self.filter_expression.resolve(context) 801 817 except TemplateSyntaxError, e: 802 818 if not hasattr(e, 'source'): 803 819 e.source = self.source 804 820 raise 821 if context.autoescape and not isinstance(output, SafeData): 822 return escape(output) 823 else: 824 return output 805 825 806 826 def generic_tag_compiler(params, defaults, name, node_class, parser, token): 807 827 "Returns a template.Node subclass." -
django/template/context.py
diff --git a/django/template/context.py b/django/template/context.py index 59650b05fe5c1ab862fa557cf7a4642fc24c9d0e..0f229ce2ec51166719e2b3547f69cc06659d62e8 100644
a b class ContextPopException(Exception): 9 9 10 10 class Context(object): 11 11 "A stack container for variable context" 12 13 autoescape = False 14 12 15 def __init__(self, dict_=None): 13 16 dict_ = dict_ or {} 14 17 self.dicts = [dict_] … … class RequestContext(Context): 98 101 processors = tuple(processors) 99 102 for processor in get_standard_processors() + processors: 100 103 self.update(processor(request)) 104 -
django/template/defaultfilters.py
diff --git a/django/template/defaultfilters.py b/django/template/defaultfilters.py index 27a0b07df038f36db04dcb251495da37cf775cee..1b5c08812ae8cdae088db28409d5801f192d52ec 100644
a b from django.template import resolve_vari 4 4 from django.conf import settings 5 5 from django.utils.translation import ugettext, ungettext 6 6 from django.utils.encoding import force_unicode, smart_str, iri_to_uri 7 from django.utils.safestring import mark_safe, SafeData 7 8 import re 8 9 import random as random_module 9 10 … … def addslashes(value): 39 40 "Adds slashes - useful for passing strings to JavaScript, for example." 40 41 return value.replace('\\', '\\\\').replace('"', '\\"').replace("'", "\\'") 41 42 addslashes = stringfilter(addslashes) 43 addslashes.is_safe = True 42 44 43 45 def capfirst(value): 44 46 "Capitalizes the first character of the value" 45 47 return value and value[0].upper() + value[1:] 46 48 capfirst = stringfilter(capfirst) 49 capfirst.is_safe = True 47 50 48 51 def fix_ampersands(value): 49 52 "Replaces ampersands with ``&`` entities" 50 53 from django.utils.html import fix_ampersands 51 54 return fix_ampersands(value) 52 55 fix_ampersands = stringfilter(fix_ampersands) 56 fix_ampersands.is_safe = True 53 57 54 58 def floatformat(text, arg=-1): 55 59 """ … … def floatformat(text, arg=-1): 83 87 return u'%d' % int(f) 84 88 else: 85 89 formatstr = u'%%.%df' % abs(d) 86 return formatstr % f 90 return mark_safe(formatstr % f) 91 floatformat.is_safe = True 87 92 88 93 def iriencode(value): 89 94 "Escapes an IRI value for use in a URL" 90 95 return force_unicode(iri_to_uri(value)) 91 96 iriencode = stringfilter(iriencode) 92 97 93 def linenumbers(value ):98 def linenumbers(value, autoescape = None): 94 99 "Displays text with line numbers" 95 100 from django.utils.html import escape 96 101 lines = value.split(u'\n') 97 102 # Find the maximum width of the line count, for use with zero padding string format command 98 103 width = unicode(len(unicode(len(lines)))) 99 for i, line in enumerate(lines): 100 lines[i] = (u"%0" + width + u"d. %s") % (i + 1, escape(line)) 101 return u'\n'.join(lines) 104 if not autoescape or isinstance(value, SafeData): 105 for i, line in enumerate(lines): 106 lines[i] = (u"%0" + width + u"d. %s") % (i + 1, line) 107 else: 108 for i, line in enumerate(lines): 109 lines[i] = (u"%0" + width + u"d. %s") % (i + 1, escape(line)) 110 return mark_safe(u'\n'.join(lines)) 102 111 linenumbers = stringfilter(linenumbers) 112 linenumbers.is_safe = True 113 linenumbers.needs_autoescape = True 103 114 104 115 def lower(value): 105 116 "Converts a string into all lowercase" 106 117 return value.lower() 107 118 lower = stringfilter(lower) 119 lower.is_safe = True 108 120 109 121 def make_list(value): 110 122 """ … … def make_list(value): 113 125 """ 114 126 return list(value) 115 127 make_list = stringfilter(make_list) 128 make_list.is_safe = False 116 129 117 130 def slugify(value): 118 131 "Converts to lowercase, removes non-alpha chars and converts spaces to hyphens" 119 132 # Don't compile patterns as unicode because \w then would mean any letter. Slugify is effectively an asciiization. 120 133 value = re.sub('[^\w\s-]', '', value).strip().lower() 121 return re.sub('[-\s]+', '-', value)134 return mark_safe(re.sub('[-\s]+', '-', value)) 122 135 slugify = stringfilter(slugify) 136 slugify.is_safe = True 123 137 124 138 def stringformat(value, arg): 125 139 """ … … def stringformat(value, arg): 134 148 return (u"%" + unicode(arg)) % value 135 149 except (ValueError, TypeError): 136 150 return u"" 151 stringformat.is_safe = True 137 152 138 153 def title(value): 139 154 "Converts a string into titlecase" 140 155 return re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title()) 141 156 title = stringfilter(title) 157 title.is_safe = False 142 158 143 159 def truncatewords(value, arg): 144 160 """ … … def truncatewords(value, arg): 153 169 return value # Fail silently. 154 170 return truncate_words(value, length) 155 171 truncatewords = stringfilter(truncatewords) 172 truncatewords.is_safe = True 156 173 157 174 def truncatewords_html(value, arg): 158 175 """ … … def upper(value): 172 189 "Converts a string into all uppercase" 173 190 return value.upper() 174 191 upper = stringfilter(upper) 192 upper.is_safe = False 175 193 176 194 def urlencode(value): 177 195 "Escapes a value for use in a URL" 178 196 import urllib 179 197 return force_unicode(urllib.quote(value)) 180 198 urlencode = stringfilter(urlencode) 199 urlencode.is_safe = False 181 200 182 201 def urlize(value): 183 202 "Converts URLs in plain text into clickable links" 184 203 from django.utils.html import urlize 185 return urlize(value, nofollow=True)204 return mark_safe(urlize(value, nofollow=True)) 186 205 urlize = stringfilter(urlize) 206 urlize.is_safe = True 187 207 188 208 def urlizetrunc(value, limit): 189 209 """ … … def urlizetrunc(value, limit): 193 213 Argument: Length to truncate URLs to. 194 214 """ 195 215 from django.utils.html import urlize 196 return urlize(value, trim_url_limit=int(limit), nofollow=True)216 return mark_safe(urlize(value, trim_url_limit=int(limit), nofollow=True)) 197 217 urlizetrunc = stringfilter(urlizetrunc) 218 urlize.is_safe = True 198 219 199 220 def wordcount(value): 200 221 "Returns the number of words" 201 222 return len(value.split()) 202 223 wordcount = stringfilter(wordcount) 224 wordcount.is_safe = False 203 225 204 226 def wordwrap(value, arg): 205 227 """ … … def wordwrap(value, arg): 210 232 from django.utils.text import wrap 211 233 return wrap(value, int(arg)) 212 234 wordwrap = stringfilter(wordwrap) 235 wordwrap.is_safe = True 213 236 214 237 def ljust(value, arg): 215 238 """ … … def ljust(value, arg): 219 242 """ 220 243 return value.ljust(int(arg)) 221 244 ljust = stringfilter(ljust) 245 ljust.is_safe = True 222 246 223 247 def rjust(value, arg): 224 248 """ … … def rjust(value, arg): 228 252 """ 229 253 return value.rjust(int(arg)) 230 254 rjust = stringfilter(rjust) 255 rjust.is_safe = True 231 256 232 257 def center(value, arg): 233 258 "Centers the value in a field of a given width" 234 259 return value.center(int(arg)) 235 260 center = stringfilter(center) 261 center.is_safe = True 236 262 237 263 def cut(value, arg): 238 264 "Removes all values of arg from the given string" 239 265 return value.replace(arg, u'') 240 266 cut = stringfilter(cut) 267 cut.is_safe = False 241 268 242 269 ################### 243 270 # HTML STRINGS # 244 271 ################### 245 272 246 273 def escape(value): 247 "Escapes a string's HTML" 274 "Marks the value as a string that should not be auto-escaped." 275 from django.utils.safestring import mark_for_escaping 276 return mark_for_escaping(value) 277 escape = stringfilter(escape) 278 escape.is_safe = True 279 280 def force_escape(value): 281 """Escapes a string's HTML. This returns a new string containing the escaped 282 characters (as opposed to "escape", which marks the content for later 283 possible escaping).""" 248 284 from django.utils.html import escape 249 return escape(value)285 return mark_safe(escape(value)) 250 286 escape = stringfilter(escape) 287 force_escape.is_safe = True 251 288 252 def linebreaks(value ):289 def linebreaks(value, autoescape = None): 253 290 "Converts newlines into <p> and <br />s" 254 291 from django.utils.html import linebreaks 255 return linebreaks(value) 292 autoescape = autoescape and not isinstance(value, SafeData) 293 return mark_safe(linebreaks(value, autoescape)) 256 294 linebreaks = stringfilter(linebreaks) 295 linebreaks.is_safe = True 296 linebreaks.needs_autoescape = True 257 297 258 def linebreaksbr(value ):298 def linebreaksbr(value, autoescape = None): 259 299 "Converts newlines into <br />s" 260 return value.replace('\n', '<br />') 300 if autoescape and not isinstance(value, SafeData): 301 from django.utils.html import escape 302 data = escape(value) 303 else: 304 data = value 305 return mark_safe(data.replace('\n', '<br />')) 261 306 linebreaksbr = stringfilter(linebreaksbr) 307 linebreaksbr.is_safe = True 308 linebreaksbr.needs_autoescape = True 309 310 def safe(value): 311 "Marks the value as a string that should not be auto-escaped." 312 from django.utils.safestring import mark_safe 313 return mark_safe(value) 314 safe = stringfilter(safe) 315 safe.is_safe = True 262 316 263 317 def removetags(value, tags): 264 318 "Removes a space separated list of [X]HTML tags from the output" … … def removetags(value, tags): 270 324 value = endtag_re.sub(u'', value) 271 325 return value 272 326 removetags = stringfilter(removetags) 327 removetags.is_safe = True 273 328 274 329 def striptags(value): 275 330 "Strips all [X]HTML tags" 276 331 from django.utils.html import strip_tags 277 332 return strip_tags(value) 278 333 striptags = stringfilter(striptags) 334 striptags.is_safe = True 279 335 280 336 ################### 281 337 # LISTS # … … def dictsort(value, arg): 289 345 decorated = [(resolve_variable(u'var.' + arg, {u'var' : item}), item) for item in value] 290 346 decorated.sort() 291 347 return [item[1] for item in decorated] 348 dictsort.is_safe = False 292 349 293 350 def dictsortreversed(value, arg): 294 351 """ … … def dictsortreversed(value, arg): 299 356 decorated.sort() 300 357 decorated.reverse() 301 358 return [item[1] for item in decorated] 359 dictsortreversed.is_safe = False 302 360 303 361 def first(value): 304 362 "Returns the first item in a list" … … def first(value): 306 364 return value[0] 307 365 except IndexError: 308 366 return u'' 367 first.is_safe = True 309 368 310 369 def join(value, arg): 311 370 "Joins a list with a string, like Python's ``str.join(list)``" 312 371 try: 313 returnarg.join(map(force_unicode, value))372 data = arg.join(map(force_unicode, value)) 314 373 except AttributeError: # fail silently but nicely 315 374 return value 375 safe_args = reduce(lambda lhs, rhs: lhs and isinstance(rhs, SafeData), value, True) 376 if safe_args: 377 return mark_safe(data) 378 else: 379 return data 380 join.is_safe = True 316 381 317 382 def length(value): 318 383 "Returns the length of the value - useful for lists" 319 384 return len(value) 385 length.is_safe = False 320 386 321 387 def length_is(value, arg): 322 388 "Returns a boolean of whether the value's length is the argument" 323 389 return len(value) == int(arg) 390 length.is_safe = False 324 391 325 392 def random(value): 326 393 "Returns a random item from the list" 327 394 return random_module.choice(value) 395 length.is_safe = True 328 396 329 397 def slice_(value, arg): 330 398 """ … … def slice_(value, arg): 345 413 346 414 except (ValueError, TypeError): 347 415 return value # Fail silently. 416 slice_.is_safe = True 348 417 349 def unordered_list(value ):418 def unordered_list(value, autoescape = None): 350 419 """ 351 420 Recursively takes a self-nested list and returns an HTML unordered list -- 352 421 WITHOUT opening and closing <ul> tags. … … def unordered_list(value): 367 436 </ul> 368 437 </li> 369 438 """ 439 if autoescape: 440 from django.utils.html import conditional_escape 441 escaper = conditional_escape 442 else: 443 escaper = lambda x: x 444 370 445 def _helper(value, tabs): 371 446 indent = u'\t' * tabs 372 447 if value[1]: 373 return u'%s<li>%s\n%s<ul>\n%s\n%s</ul>\n%s</li>' % (indent, force_unicode(value[0]), indent,448 return u'%s<li>%s\n%s<ul>\n%s\n%s</ul>\n%s</li>' % (indent, escaper(force_unicode(value[0])), indent, 374 449 u'\n'.join([_helper(v, tabs+1) for v in value[1]]), indent, indent) 375 450 else: 376 return u'%s<li>%s</li>' % (indent, force_unicode(value[0])) 377 return _helper(value, 1) 451 return u'%s<li>%s</li>' % (indent, escaper(force_unicode(value[0]))) 452 return mark_safe(_helper(value, 1)) 453 unordered_list.is_safe = True 454 unordered_list.needs_autoescape = True 378 455 379 456 ################### 380 457 # INTEGERS # … … ################### 383 460 def add(value, arg): 384 461 "Adds the arg to the value" 385 462 return int(value) + int(arg) 463 add.is_safe = False 386 464 387 465 def get_digit(value, arg): 388 466 """ … … def get_digit(value, arg): 402 480 return int(str(value)[-arg]) 403 481 except IndexError: 404 482 return 0 483 get_digit.is_safe = False 405 484 406 485 ################### 407 486 # DATES # … … def date(value, arg=None): 415 494 if arg is None: 416 495 arg = settings.DATE_FORMAT 417 496 return format(value, arg) 497 date.is_safe = False 418 498 419 499 def time(value, arg=None): 420 500 "Formats a time according to the given format" … … def time(value, arg=None): 424 504 if arg is None: 425 505 arg = settings.TIME_FORMAT 426 506 return time_format(value, arg) 507 time.is_safe = False 427 508 428 509 def timesince(value, arg=None): 429 510 'Formats a date as the time since that date (i.e. "4 days, 6 hours")' … … def timesince(value, arg=None): 433 514 if arg: 434 515 return timesince(arg, value) 435 516 return timesince(value) 517 timesince.is_safe = False 436 518 437 519 def timeuntil(value, arg=None): 438 520 'Formats a date as the time until that date (i.e. "4 days, 6 hours")' … … def timeuntil(value, arg=None): 443 525 if arg: 444 526 return timesince(arg, value) 445 527 return timesince(datetime.now(), value) 528 timeuntil.is_safe = False 446 529 447 530 ################### 448 531 # LOGIC # … … ################### 451 534 def default(value, arg): 452 535 "If value is unavailable, use given default" 453 536 return value or arg 537 default.is_safe = False 454 538 455 539 def default_if_none(value, arg): 456 540 "If value is None, use given default" 457 541 if value is None: 458 542 return arg 459 543 return value 544 default_if_none.is_safe = False 460 545 461 546 def divisibleby(value, arg): 462 547 "Returns true if the value is devisible by the argument" 463 548 return int(value) % int(arg) == 0 549 divisibleby.is_safe = False 464 550 465 551 def yesno(value, arg=None): 466 552 """ … … def yesno(value, arg=None): 491 577 if value: 492 578 return yes 493 579 return no 580 yesno.is_safe = False 494 581 495 582 ################### 496 583 # MISC # … … def filesizeformat(bytes): 513 600 if bytes < 1024 * 1024 * 1024: 514 601 return ugettext("%.1f MB") % (bytes / (1024 * 1024)) 515 602 return ugettext("%.1f GB") % (bytes / (1024 * 1024 * 1024)) 603 filesizeformat.is_safe = True 516 604 517 605 def pluralize(value, arg=u's'): 518 606 """ … … def pluralize(value, arg=u's'): 540 628 except TypeError: # len() of unsized object 541 629 pass 542 630 return singular_suffix 631 pluralize.is_safe = False 543 632 544 633 def phone2numeric(value): 545 634 "Takes a phone number and converts it in to its numerical equivalent" 546 635 from django.utils.text import phone2numeric 547 636 return phone2numeric(value) 637 phone2numeric.is_safe = True 548 638 549 639 def pprint(value): 550 640 "A wrapper around pprint.pprint -- for debugging, really" … … def pprint(value): 553 643 return pformat(value) 554 644 except Exception, e: 555 645 return u"Error in formatting:%s" % force_unicode(e) 646 pprint.is_safe = True 556 647 557 648 # Syntax: register.filter(name of filter, callback) 558 649 register.filter(add) … … register.filter(filesizeformat) 571 662 register.filter(first) 572 663 register.filter(fix_ampersands) 573 664 register.filter(floatformat) 665 register.filter(force_escape) 574 666 register.filter(get_digit) 575 667 register.filter(iriencode) 576 668 register.filter(join) … … register.filter(pprint) 588 680 register.filter(removetags) 589 681 register.filter(random) 590 682 register.filter(rjust) 683 register.filter(safe) 591 684 register.filter('slice', slice_) 592 685 register.filter(slugify) 593 686 register.filter(stringformat) -
django/template/defaulttags.py
diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py index f2453dff4ac7b556dbe8b5b73dc1b9d42dd82f04..83ee973213ebd85e3db054e56a403d7ee1c8c667 100644
a b from django.template import TemplateSynt 5 5 from django.template import get_library, Library, InvalidTemplateLibrary 6 6 from django.conf import settings 7 7 from django.utils.encoding import smart_str 8 from django.utils.safestring import mark_safe 8 9 import sys 9 10 10 11 register = Library() 11 12 13 class AutoEscapeControlNode(Node): 14 """Implements the actions of both the autoescape and noautescape tags.""" 15 def __init__(self, setting, nodelist): 16 self.setting, self.nodelist = setting, nodelist 17 18 def render(self, context): 19 old_setting = context.autoescape 20 context.autoescape = self.setting 21 output = self.nodelist.render(context) 22 context.autoescape = old_setting 23 if self.setting: 24 return mark_safe(output) 25 else: 26 return output 27 12 28 class CommentNode(Node): 13 29 def render(self, context): 14 30 return '' … … class RegroupNode(Node): 238 254 return '' 239 255 output = [] # list of dictionaries in the format {'grouper': 'key', 'list': [list of contents]} 240 256 for obj in obj_list: 241 grouper = self.expression.resolve(Context({'var': obj}), True) 257 ctxt = Context({'var': obj}) 258 ctxt.autoescape = context.autoescape 259 grouper = self.expression.resolve(ctxt, True) 242 260 # TODO: Is this a sensible way to determine equality? 243 261 if output and repr(output[-1]['grouper']) == repr(grouper): 244 262 output[-1]['list'].append(obj) … … class WithNode(Node): 376 394 return output 377 395 378 396 #@register.tag 397 def autoescape(parser, token): 398 """ 399 Force autoescape behaviour for this block. 400 """ 401 nodelist = parser.parse(('endautoescape',)) 402 parser.delete_first_token() 403 return AutoEscapeControlNode(True, nodelist) 404 autoescape = register.tag(autoescape) 405 406 #@register.tag 379 407 def comment(parser, token): 380 408 """ 381 409 Ignore everything between ``{% comment %}`` and ``{% endcomment %}`` … … def do_filter(parser, token): 478 506 479 507 Sample usage:: 480 508 481 {% filter escape|lower %}509 {% filter force_escape|lower %} 482 510 This text will be HTML-escaped, and will appear in lowercase. 483 511 {% endfilter %} 484 512 """ 485 513 _, rest = token.contents.split(None, 1) 486 514 filter_expr = parser.compile_filter("var|%s" % (rest)) 515 for func, unused in filter_expr.filters: 516 if getattr(func, '_decorated_function', func).__name__ in ('escape', 'safe'): 517 raise TemplateSyntaxError('"filter %s" is not permitted. Use the "autoescape" tag instead.' % func.__name__) 487 518 nodelist = parser.parse(('endfilter',)) 488 519 parser.delete_first_token() 489 520 return FilterNode(filter_expr, nodelist) … … def ifchanged(parser, token): 725 756 ifchanged = register.tag(ifchanged) 726 757 727 758 #@register.tag 759 def noautoescape(parser, token): 760 """ 761 Force autoescape behaviour to be disabled for this block. 762 """ 763 nodelist = parser.parse(('endnoautoescape',)) 764 parser.delete_first_token() 765 return AutoEscapeControlNode(False, nodelist) 766 autoescape = register.tag(noautoescape) 767 768 #@register.tag 728 769 def ssi(parser, token): 729 770 """ 730 771 Output the contents of a given file into the page. -
django/utils/encoding.py
diff --git a/django/utils/encoding.py b/django/utils/encoding.py index 8601dc44f3b60aacef2bbf5b209d60f32808e4df..d88d69931caabd8555fea15a784a751f0010943b 100644
a b def force_unicode(s, encoding='utf-8', e 33 33 else: 34 34 s = unicode(str(s), encoding, errors) 35 35 elif not isinstance(s, unicode): 36 s = unicode(s,encoding, errors)36 s = s.decode(encoding, errors) 37 37 return s 38 38 39 39 def smart_str(s, encoding='utf-8', strings_only=False, errors='strict'): -
django/utils/html.py
diff --git a/django/utils/html.py b/django/utils/html.py index 1dde298c297a8d4bbd24d8c53334a587dd789c54..2fb8298e458364f8b3f2614df6966d328f2e4c7c 100644
a b 3 3 import re 4 4 import string 5 5 import urllib 6 from django.utils.encoding import smart_unicode 7 from django.utils.safestring import SafeData 6 8 from django.utils.encoding import force_unicode, smart_str 7 9 from django.utils.functional import allow_lazy 8 10 … … def escape(html): 30 32 return force_unicode(html).replace('&', '&').replace('<', '<').replace('>', '>').replace('"', '"').replace("'", ''') 31 33 escape = allow_lazy(escape, unicode) 32 34 33 def linebreaks(value): 35 def conditional_escape(html): 36 "Similar to escape(), except that it does not operate on pre-escaped strings" 37 if isinstance(html, SafeData): 38 return html 39 else: 40 return escape(html) 41 42 def linebreaks(value, autoescape = False): 34 43 "Converts newlines into <p> and <br />s" 35 44 value = re.sub(r'\r\n|\r|\n', '\n', force_unicode(value)) # normalize newlines 36 45 paras = re.split('\n{2,}', value) 37 paras = [u'<p>%s</p>' % p.strip().replace('\n', '<br />') for p in paras] 46 if autoescape: 47 paras = [u'<p>%s</p>' % escape(p.strip()).replace('\n', '<br />') for p in paras] 48 else: 49 paras = [u'<p>%s</p>' % p.strip().replace('\n', '<br />') for p in paras] 38 50 return u'\n\n'.join(paras) 39 51 linebreaks = allow_lazy(linebreaks, unicode) 40 52 -
new file django/utils/safestring.py
diff --git a/django/utils/safestring.py b/django/utils/safestring.py new file mode 100644 index 0000000000000000000000000000000000000000..18c931b56a20645660b26e1bf3e05ebb57b8b11d
- + 1 """ 2 Functions for working with "safe strings": strings that can be displayed safely 3 without further escaping in HTML. Here, a "safe string" means that the producer 4 of the string has already turned characters that should not be interpreted by 5 the HTML engine (e.g. '<') into the appropriate entities. 6 """ 7 from django.utils.functional import curry 8 from django.utils.encoding import force_unicode 9 10 class EscapeData(object): 11 pass 12 13 class EscapeString(str, EscapeData): 14 """ 15 A string that should be HTML-escaped when output. 16 """ 17 pass 18 19 class EscapeUnicode(unicode, EscapeData): 20 """ 21 A unicode object that should be HTML-escaped when output. 22 """ 23 pass 24 25 class SafeData(object): 26 pass 27 28 class SafeString(str, SafeData): 29 """ 30 A string subclass that has been specifically marked as "safe" for HTML 31 output purposes. 32 """ 33 def __add__(self, rhs): 34 """ 35 Concatenating a safe string with another safe string or safe unicode 36 object is safe. Otherwise, the result is no longer safe. 37 """ 38 if isinstance(rhs, SafeUnicode): 39 return SafeUnicode(self + rhs) 40 elif isinstance(rhs, SafeString): 41 return SafeString(self + rhs) 42 else: 43 return super(SafeString, self).__add__(rhs) 44 45 def __str__(self): 46 return self 47 48 def _proxy_method(self, *args, **kwargs): 49 """ 50 Wrap a call to a normal unicode method up so that we return safe 51 results. The method that is being wrapped is passed in the 'method' 52 argument. 53 """ 54 method = kwargs.pop('method') 55 data = method(self, *args, **kwargs) 56 if isinstance(data, str): 57 return SafeString(data) 58 else: 59 return SafeUnicode(data) 60 61 encode = curry(_proxy_method, method = str.encode) 62 decode = curry(_proxy_method, method = str.decode) 63 64 class SafeUnicode(unicode, SafeData): 65 """ 66 A unicode subclass that has been specifically marked as "safe" for HTML 67 output purposes. 68 """ 69 def __add__(self, rhs): 70 """ 71 Concatenating a safe unicode object with another safe string or safe 72 unicode object is safe. Otherwise, the result is no longer safe. 73 """ 74 if isinstance(rhs, SafeData): 75 return SafeUnicode(self + rhs) 76 else: 77 return super(SafeUnicode, self).__add__(rhs) 78 79 def _proxy_method(self, *args, **kwargs): 80 """ 81 Wrap a call to a normal unicode method up so that we return safe 82 results. The method that is being wrapped is passed in the 'method' 83 argument. 84 """ 85 method = kwargs.pop('method') 86 data = method(self, *args, **kwargs) 87 if isinstance(data, str): 88 return SafeString(data) 89 else: 90 return SafeUnicode(data) 91 92 encode = curry(_proxy_method, method = unicode.encode) 93 decode = curry(_proxy_method, method = unicode.decode) 94 95 96 def mark_safe(s): 97 """ 98 Explicitly mark a string as safe for (HTML) output purposes. The returned 99 object can be used everywhere a string or unicode object is appropriate. 100 101 Can safely be called multiple times on a single string. 102 """ 103 if isinstance(s, SafeData): 104 return s 105 if isinstance(s, str): 106 return SafeString(s) 107 if isinstance(s, unicode): 108 return SafeUnicode(s) 109 return SafeString(str(s)) 110 111 def mark_for_escaping(s): 112 """ 113 Explicitly mark a string as requiring HTML escaping upon output. Has no 114 effect on SafeData subclasses. 115 116 Can be safely called multiple times on a single string (the effect is only 117 applied once). 118 """ 119 if isinstance(s, SafeData) or isinstance(s, EscapeData): 120 return s 121 if isinstance(s, str): 122 return EscapeString(s) 123 if isinstance(s, unicode): 124 return EscapeUnicode(s) 125 return EscapeString(str(s)) 126 -
docs/templates.txt
diff --git a/docs/templates.txt b/docs/templates.txt index 8fe4c82d43460be2e347eaa878c0cbbbaaad6b49..36888bd60f9cc37646e0f38bf2af1a25a0918576 100644
a b it also defines the content that fills t 268 268 two similarly-named ``{% block %}`` tags in a template, that template's parent 269 269 wouldn't know which one of the blocks' content to use. 270 270 271 Automatic HTML escaping 272 ======================= 273 274 A very real problem when creating HTML (and other) output using templates and 275 variable substitution is the possibility of accidently inserting some variable 276 value that affects the resulting HTML. For example, a template fragment like 277 278 :: 279 280 Hello, {{ name }}. 281 282 seems like a harmless way to display the user's name. However, if you are 283 displaying data that the user entered directly and they entered their name as 284 285 :: 286 287 <script>alert('hello')</script> 288 289 this would always display a Javascript alert box whenever the page was loaded. 290 Similarly, if you were displaying some data generated by another process and 291 it contained a '<' symbol, you couldn't just dump this straight into your 292 HTML, because it would be treated as the start of an element. The effects of 293 these sorts of problems can vary from merely annoying to allowing exploits via 294 `Cross Site Scripting`_ (XSS) attacks. 295 296 .. _Cross Site Scripting: http://en.wikipedia.org/wiki/Cross-site_scripting 297 298 In order to provide some protection against these problems, Django provides an 299 auto-escaping template tag. Inside this tag, any data that comes from template 300 variables is examined to see if it contains one of the five HTML characters 301 (<, >, ', " and &) that often need escaping and those characters are converted 302 to their respective HTML entities. 303 304 Because some variables will contain data that is *intended* to be rendered 305 as HTML, template tag and filter writers can mark their output strings as 306 requiring no further escaping. For example, the ``unordered_list`` filter is 307 designed to return raw HTML and we want the template processor to simply 308 display the results as returned, without applying any escaping. That is taken 309 care of by the filter. The template author need do nothing special in that 310 case. 311 312 By default, auto-escaping is not in effect. To enable it inside your template, 313 wrap the affected content in the ``autoescape`` tag, like so:: 314 315 {% autoescape %} 316 Hello {{ name }} 317 {% endautoescape %} 318 319 Since the auto-escaping tag passes its effect onto templates that extend the 320 current one as well as templates included via the ``include`` tag (just like 321 all block tags), if you wrap your main HTML content in an ``autoescape`` tag, 322 you will have automatic escaping applied to all of your content. 323 324 At times, you might want to disable auto-escaping when it would otherwise be 325 in effect. You can do this with the ``noautoescape`` tag. For example:: 326 327 {% autoescape %} 328 Hello {{ name }} 329 330 {% noautoescape %} 331 This will not be auto-escaped: {{ data }}. 332 333 Nor this: {{ other_data }} 334 {% endnoautoescape %} 335 {% endautoescape %} 336 337 For individual variables, the ``safe`` filter can also be used. 338 339 Generally, you will not need to worry about auto-escaping very much. Enable it 340 in your base template once you are entering the main HTML region and then 341 write your templates normally. The view developers and custom filter authors 342 need to think about when their data should not be escaped and mark it 343 appropriately. They are in a better position to know when that should happen 344 than the template author, so it is their responsibility. By default, when 345 auto-escaping is enabled, all output is escaped unless the template processor 346 is explicitly told otherwise. 347 271 348 Using the built-in reference 272 349 ============================ 273 350 … … available, and what they do. 343 420 Built-in tag reference 344 421 ---------------------- 345 422 423 autoescape 424 ~~~~~~~~~~ 425 426 All variables that are output inside this tag have HTML escaping applied to 427 them, as if they each had the ``escape`` filter attached to them. 428 429 The only exceptions are variables that are already marked as 'safe' from 430 escaping, either by the code that populated the variable, or because it has 431 the ``safe`` filter applied. 432 346 433 block 347 434 ~~~~~ 348 435 … … just like in variable syntax. 410 497 411 498 Sample usage:: 412 499 413 {% filter escape|lower %}500 {% filter force_escape|lower %} 414 501 This text will be HTML-escaped, and will appear in all lowercase. 415 502 {% endfilter %} 416 503 … … Load a custom template tag set. 625 712 626 713 See `Custom tag and filter libraries`_ for more information. 627 714 715 noautoescape 716 ~~~~~~~~~~~~ 717 718 Disable the effects of the ``autoescape`` tag (if it is in effect). 719 628 720 now 629 721 ~~~ 630 722 … … Escapes a string's HTML. Specifically, i 962 1054 * ``'"'`` (double quote) to ``'"'`` 963 1055 * ``"'"`` (single quote) to ``'''`` 964 1056 1057 The escaping is only applied when the string is output, so it does not matter 1058 where in a chained sequence of filters you put ``escape``: it will always be 1059 applied as though it were the last filter. If you want escaping to be applied 1060 immediately, use the ``force_escape`` filter. 1061 965 1062 filesizeformat 966 1063 ~~~~~~~~~~~~~~ 967 1064 … … For example: 1004 1101 Using ``floatformat`` with no argument is equivalent to using ``floatformat`` with 1005 1102 an argument of ``-1``. 1006 1103 1104 force_escape 1105 ~~~~~~~~~~~~ 1106 1107 **New in Django development version** 1108 1109 Applies HTML escaping to a string (see the ``escape`` filter for details). 1110 This filter is applied immediately and returns a new, escaped string. This is 1111 useful in the typically rare cases where you need multiple escaping or want to 1112 apply other filters to the escaped results. Normally, you want to use the 1113 ``escape`` filter. 1114 1007 1115 get_digit 1008 1116 ~~~~~~~~~ 1009 1117 … … Right-aligns the value in a field of a g 1125 1233 1126 1234 **Argument:** field size 1127 1235 1236 safe 1237 ~~~~ 1238 1239 Marks a string as not requiring further HTML escaping prior to output. This is 1240 only useful inside an ``autoescape`` block, when the output would otherwise be 1241 automatically escaped. Outside of an ``autoescape`` block, this filter has no 1242 effect. 1243 1128 1244 slice 1129 1245 ~~~~~ 1130 1246 -
docs/templates_python.txt
diff --git a/docs/templates_python.txt b/docs/templates_python.txt index 08a287f572d40cb0a628e144c2824a8137f4d6f9..f3181e6c688e1e5a0b7d4be0ac46ad95b9aa8df4 100644
a b an object to it's string value before be 669 669 def lower(value): 670 670 return value.lower() 671 671 672 Filters and auto-escaping 673 ~~~~~~~~~~~~~~~~~~~~~~~~~ 674 675 When you are writing a custom filter, you need to give some thought to how 676 this filter will work when rendered in an auto-escaping environment (inside 677 an ``autoescape`` template tag block). First, you should realise that there 678 are three types of strings that can be passed around inside the template code: 679 680 * raw strings are the native Python ``str`` (or ``unicode``) types. On 681 output, they are escaped if they are inside an ``autoescape`` block. 682 * "safe" strings are strings that are safe from further escaping at output 683 time. Any necessary escaping has already been done. They are commonly used 684 for output that contains raw HTML that is intended to be intrepreted on the 685 client side. 686 687 Internally, these strings are of type ``SafeString`` or ``SafeUnicode``, 688 although they share a common base class in ``SafeData``, so you can test 689 for them using code like:: 690 691 if isinstance(value, SafeData): 692 # Do something with the "safe" string. 693 694 * strings which are marked as "need escaping" are *always* escaped on 695 output, regardless of whether they are in an ``autoescape`` block or not. 696 These strings are only escaped once, however, even if used inside an 697 ``autoescaep`` block. This type of string is internally represented by the 698 types ``EscapeString`` and ``EscapeUnicode``. You will not normally need to 699 worry about these; they exist only for the implementation of the ``escape`` 700 filter. 701 702 Inside your filter, you will need to think about three areas in order to be 703 auto-escaping compliant: 704 705 1. If your filter returns a string that is ready for direct output (it should 706 be considered a "safe" string), you should call 707 ``django.utils.safestring.mark_safe()`` on the result prior to returning. 708 This will turn the result into the appropriate ``SafeData`` type. 709 710 2. If your filter is given a "safe" string, is it guaranteed to return a 711 "safe" string? If so, set the ``is_safe`` attribute on the function to be 712 ``True``. For example, a filter that replaced all numbers with the number 713 spelt out in words is going to be safe-string-preserving, since it cannot 714 introduce any of the five dangerous characters: <, >, ", ' or &. So we can 715 write:: 716 717 @register.filter 718 def convert_to_words(value): 719 # ... implementation here ... 720 return result 721 722 convert_to_words.is_safe = True 723 724 Note that this filter does not return a universally safe result (it does not 725 return ``mark_safe(result)``) because if it is handed a raw string such as 726 '<a>', this will need further escaping in an auto-escape environment. The 727 ``is_safe`` attribute only talks about the safeness of the result when a safe 728 string is passed in to the filter. 729 730 3. Will your filter behave differently depending upon whether auto-escaping 731 is currently in effect or not? For example, the ``ordered_list`` filter that 732 ships with Django needs to know whether to escape its content or not. It will 733 always return a safe string, since it returns raw HTML, so we cannot apply 734 escaping to the result -- it needs to be done in-situ. 735 736 For these cases, the filter function needs to be told what the current 737 auto-escaping setting is. Set the ``needs_autoescape`` attribute on the 738 filter to ``True`` and have your function take an extra argument called 739 ``autoescape`` with a default value of ``None``. When the filter is called, 740 the ``autoescape`` keyword argument will be ``True`` if auto-escaping is in 741 effect. For example, the ``unordered_list`` filter is written as:: 742 743 def unordered_list(value, autoescape = None): 744 # ... lots of code here ... 745 746 return mark_safe(...) 747 748 unordered_list.is_safe = True 749 unordered_list.needs_autoescape = True 750 751 By default, both the ``is_safe`` and ``needs_autoescape`` attributes are 752 ``False``. You do not need to specify them if ``False`` is an acceptable 753 value. 754 755 As a matter of convention, we leave ``is_safe`` as ``False`` for filters that 756 do not accept string inputs (they might take a number as an input, for 757 example) or for those that return a non-string (e.g. the ``length`` filter). 758 However, not following this convention will not cause any harm or make your 759 results any more vulnerable to cross-site scripting problems. 760 672 761 Writing custom template tags 673 762 ---------------------------- 674 763 … … Ultimately, this decoupling of compilati 787 876 efficient template system, because a template can render multiple context 788 877 without having to be parsed multiple times. 789 878 879 Auto-escaping considerations 880 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 881 882 The output from template tags is not automatically run through the 883 auto-escaping filters if used inside an ``autoescape`` tag. However, there are 884 still a couple of things you should keep in mind when writing a template tag: 885 886 If the ``render()`` function of your template stores the result in a context 887 variable (rather than returning the result in a string), it should take care 888 to call ``mark_safe()`` if appropriate. When the variable is ultimately 889 rendered, it will be affected by the auto-escape setting in effect at the 890 time, so content that should be safe from further escaping needs to be marked 891 as such. 892 893 Also, if your template tag creates a new context for performing some 894 sub-rendering, you should be careful to set the auto-escape variable to the 895 current context's value. For example:: 896 897 def render(self, context): 898 # ... 899 new_context = Context({'var': obj}) 900 new_context.autoescape = context.autoescape 901 # ... Do something with new_context ... 902 903 This is not a very common situation, but it is sometimes useful (see 904 ``django.templates.defaulttags.FilterNode.render()`` for an example). 905 790 906 Registering the tag 791 907 ~~~~~~~~~~~~~~~~~~~ 792 908 -
new file tests/regressiontests/autoescape/tests.py
diff --git a/tests/regressiontests/autoescape/__init__.py b/tests/regressiontests/autoescape/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/regressiontests/autoescape/models.py b/tests/regressiontests/autoescape/models.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/regressiontests/autoescape/tests.py b/tests/regressiontests/autoescape/tests.py new file mode 100644 index 0000000000000000000000000000000000000000..d8fff6800c81bed694debbd5c97efb03d8e7109c
- + 1 from django.conf import settings 2 3 if __name__ == '__main__': 4 # When running this file in isolation, we need to set up the configuration 5 # before importing 'template'. 6 settings.configure() 7 8 from regressiontests.templates.tests import Templates 9 from django import template 10 from django.template import loader 11 from django.utils.translation import activate, deactivate, install 12 from django.utils.tzinfo import LocalTimezone 13 from django.utils.safestring import mark_safe 14 from datetime import datetime, timedelta 15 import unittest 16 17 class AutoescapeTemplates(Templates): 18 def render(self, test_template, vals): 19 ctxt = template.Context(vals[1]) 20 # Hack for testing: force autoescaping to be in effect. 21 ctxt.autoescape = True 22 return test_template.render(ctxt) 23 24 def get_template_tests(self): 25 # We want to check all the normal template tests work when autoescaping is 26 # engaged. We just update results that would change with autoescaping and add 27 # in new tests. 28 29 TEMPLATE_TESTS = super(AutoescapeTemplates, self).get_template_tests() 30 31 # SYNTAX -- 32 # 'template_name': ('template contents', 'context dict', 'expected string output' or Exception class) 33 TEMPLATE_TESTS.update({ 34 35 ### BASIC SYNTAX ########################################################## 36 37 # Escaped string as argument (this test replaces the non-escaped version) 38 'basic-syntax30': (r'{{ var|default_if_none:" endquote\" hah" }}', {"var": None}, ' endquote" hah'), 39 40 # We are simulating being in a block that has inherited auto-escaping, so 41 # it is applied by default in all these tests. 42 'autoescape-basic01': ("{{ first }}", {"first": "<b>first</b>"}, "<b>first</b>"), 43 44 # Strings (ASCII or unicode) already marked as "safe" are not auto-escaped 45 'autoescape-basic02': ("{{ first }}", {"first": mark_safe("<b>first</b>")}, "<b>first</b>"), 46 'autoescape-basic03': ("{{ first }}", {"first": mark_safe(u"<b>Apple</b>")}, u"<b>Apple</b>"), 47 48 49 ### (NO)AUTOESCAPE TAG ################################################### 50 'autoescape-tag01': ("{% noautoescape %}hello{% endnoautoescape %}", {}, "hello"), 51 'autoescape-tag02': ("{% noautoescape %}{{ first }}{% endnoautoescape %}", {"first": "<b>hello</b>"}, "<b>hello</b>"), 52 'autoescape-tag03': ("{% autoescape %}{{ first }}{% endautoescape %}", {"first": "<b>hello</b>"}, "<b>hello</b>"), 53 54 # Noautoescape and autoescape nest in a predictable way. 55 'autoescape-tag04': ("{% noautoescape %}{{ first }} {% autoescape %}{{ first }}{% endautoescape %}{% endnoautoescape %}", {"first": "<a>"}, "<a> <a>"), 56 57 ### FILTER TAG ############################################################ 58 59 # The "safe" and "escape" filters cannot work due to internal 60 # implementation details (fortunately, the (no)autoescape block tags can be 61 # used in those cases) 62 'autoescape-filtertag01': ("{{ first }}{% filter safe %}{{ first }} x<y{% endfilter %}", {"first": "<a>"}, template.TemplateSyntaxError), 63 'autoescape-filtertag02': ("{% filter escape %}{{ first }} x<y{% endfilter %}", {"first": "<a>"}, template.TemplateSyntaxError), 64 65 ### FILTER TESTS ########################################################## 66 67 'ae-filter-addslash01': ("{{ a|addslashes }} {{ b|addslashes }}", {"a": "<a>'", "b": mark_safe("<a>'")}, r"<a>\' <a>\'"), 68 69 'ae-filter-capfirst01': ("{{ a|capfirst }} {{ b|capfirst }}", {"a": "fred>", "b": mark_safe("fred>")}, "Fred> Fred>"), 70 71 # Note that applying fix_ampsersands in autoescape mode leads to double 72 # escaping. 73 'ae-filter-fix_ampersands01': ("{{ a|fix_ampersands }} {{ b|fix_ampersands }}", {"a": "a&b", "b": mark_safe("a&b")}, "a&amp;b a&b"), 74 75 'ae-filter-floatformat01': ("{{ a|floatformat }} {{ b|floatformat }}", {"a": "1.42", "b": mark_safe("1.42")}, "1.4 1.4"), 76 77 # The contents of "linenumbers" is escaped according to the current 78 # autoescape setting. 79 'ae-filter-linenumbers01': ("{{ a|linenumbers }} {{ b|linenumbers }}", {"a": "one\n<two>\nthree", "b": mark_safe("one\n<two>\nthree")}, "1. one\n2. <two>\n3. three 1. one\n2. <two>\n3. three"), 80 'ae-filter-linenumbers02': ("{% noautoescape %}{{ a|linenumbers }} {{ b|linenumbers }}{% endnoautoescape %}", {"a": "one\n<two>\nthree", "b": mark_safe("one\n<two>\nthree")}, "1. one\n2. <two>\n3. three 1. one\n2. <two>\n3. three"), 81 82 'ae-filter-lower01': ("{{ a|lower }} {{ b|lower }}", {"a": "Apple & banana", "b": mark_safe("Apple & banana")}, "apple & banana apple & banana"), 83 84 # The make_list filter can destroy # existing encoding, so the results are 85 # escaped. 86 'ae-filter-make_list01': ("{{ a|make_list }}", {"a": mark_safe("&")}, "[u'&']"), 87 'ae-filter-make_list02': ('{{ a|make_list|stringformat:"s"|safe }}', {"a": mark_safe("&")}, "[u'&']"), 88 89 # Running slugify on a pre-escaped string leads to odd behaviour, but the 90 # result is still safe. 91 'ae-filter-slugify01': ("{{ a|slugify }} {{ b|slugify }}", {"a": "a & b", "b": mark_safe("a & b")}, "a-b a-amp-b"), 92 93 # Notice that escaping is applied *after* any filters, so the string 94 # formatting here only needs to deal with pre-escaped characters. 95 'ae-filter-stringformat01': ('.{{ a|stringformat:"5s" }}. .{{ b|stringformat:"5s" }}.', {"a": "a<b", "b": mark_safe("a<b")}, ". a<b. . a<b."), 96 97 # XXX No test for "title" filter; needs an actual object. 98 99 'ae-filter-truncatewords01': ('{{ a|truncatewords:"2" }} {{ b|truncatewords:"2"}}', {"a": "alpha & bravo", "b": mark_safe("alpha & bravo")}, "alpha & ... alpha & ..."), 100 101 # The "upper" filter messes up entities (which are case-sensitive), so it's 102 # not safe for non-escaping purposes. 103 'ae-filter-upper01': ('{{ a|upper }} {{ b|upper }}', {"a": "a & b", "b": mark_safe("a & b")}, "A & B A &AMP; B"), 104 105 'ae-filter-urlize01': ('{{ a|urlize }} {{ b|urlize }}', {"a": "http://example.com/x=&y=", "b": mark_safe("http://example.com?x=&y=")}, '<a href="http://example.com/x=&y=" rel="nofollow">http://example.com/x=&y=</a> <a href="http://example.com?x=&y=" rel="nofollow">http://example.com?x=&y=</a>'), 106 'ae-filter-urlize02': ('{{ a|urlize }}', {"a": mark_safe("a & b")}, 'a & b'), 107 108 'ae-filter-urlizetrunc01': ('{{ a|urlizetrunc:"5" }} {{ b|urlizetrunc:"5" }}', {"a": "http://example.com/x=&y=", "b": mark_safe("http://example.com?x=&y=")}, '<a href="http://example.com/x=&y=" rel="nofollow">http:...</a> <a href="http://example.com?x=&y=" rel="nofollow">http:...</a>'), 109 110 'ae-filter-wordcount01': ('{{ a|wordcount }} {{ b|wordcount }}', {"a": "a & b", "b": mark_safe("a & b")}, "3 3"), 111 112 'ae-filter-wordwrap01': ('{{ a|wordwrap:"3" }} {{ b|wordwrap:"3" }}', {"a": "a & b", "b": mark_safe("a & b")}, "a &\nb a &\nb"), 113 114 'ae-filter-ljust01': ('.{{ a|ljust:"5" }}. .{{ b|ljust:"5" }}.', {"a": "a&b", "b": mark_safe("a&b")}, ".a&b . .a&b ."), 115 116 'ae-filter-rjust01': ('.{{ a|rjust:"5" }}. .{{ b|rjust:"5" }}.', {"a": "a&b", "b": mark_safe("a&b")}, ". a&b. . a&b."), 117 118 'ae-filter-center01': ('.{{ a|center:"5" }}. .{{ b|center:"5" }}.', {"a": "a&b", "b": mark_safe("a&b")}, ". a&b . . a&b ."), 119 120 # Because "cut" might remove a leading ampersand, so the results are not 121 # safe. 122 'ae-filter-cut01': ('{{ a|cut:"x" }} {{ b|cut:"x" }}', {"a": "x&y", "b": mark_safe("x&y")}, "&y &amp;y"), 123 'ae-filter-cut02': ('{{ a|cut:"&" }} {{ b|cut:"&" }}', {"a": "x&y", "b": mark_safe("x&y")}, "xy xamp;y"), 124 125 # The "escape" filter works the same whether autoescape is on or off, but 126 # it has no effect on strings already marked as safe. 127 'ae-filter-escape01': ('{{ a|escape }} {{ b|escape }}', {"a": "x&y", "b": mark_safe("x&y")}, "x&y x&y"), 128 'ae-filter-escape02': ('{% noautoescape %}{{ a|escape }} {{ b|escape }}{% endnoautoescape %}', {"a": "x&y", "b": mark_safe("x&y")}, "x&y x&y"), 129 130 # It is only applied once, regardless of the number of times it appears in 131 # a chain. 132 'ae-filter-escape03': ('{{ a|escape|escape }}', {"a": "x&y"}, "x&y"), 133 134 # Force_escape is applied immediately. It can be used to provide 135 # double-escaping, for example. 136 'ae-filter-force-escape01': ('{{ a|force_escape }}', {"a": "x&y"}, "x&y"), 137 'ae-filter-force-escape02': ('{{ a|force_escape|force_escape }}', {"a": "x&y"}, "x&amp;y"), 138 139 # Because the result of force_escape is "safe", an additional escape filter 140 # has no effect. 141 'ae-filter-force-escape03': ('{{ a|force_escape|escape }}', {"a": "x&y"}, "x&y"), 142 143 # The contents in "linebreaks" and "linebreaksbr" are escaped according to 144 # the current autoescape setting. 145 'ae-filter-linebreaks01': ('{{ a|linebreaks }} {{ b|linebreaks }}', {"a": "x&\ny", "b": mark_safe("x&\ny")}, "<p>x&<br />y</p> <p>x&<br />y</p>"), 146 'ae-filter-linebreaks02': ('{% noautoescape %}{{ a|linebreaks }} {{ b|linebreaks }}{% endnoautoescape %}', {"a": "x&\ny", "b": mark_safe("x&\ny")}, "<p>x&<br />y</p> <p>x&<br />y</p>"), 147 148 'ae-filter-linebreaksbr01': ('{{ a|linebreaksbr }} {{ b|linebreaksbr }}', {"a": "x&\ny", "b": mark_safe("x&\ny")}, "x&<br />y x&<br />y"), 149 'ae-filter-linebreaksbr02': ('{% noautoescape %}{{ a|linebreaksbr }} {{ b|linebreaksbr }}{% endnoautoescape %}', {"a": "x&\ny", "b": mark_safe("x&\ny")}, "x&<br />y x&<br />y"), 150 151 'ae-filter-safe01': ("{{ a }} -- {{ a|safe }}", {"a": "<b>hello</b>"}, "<b>hello</b> -- <b>hello</b>"), 152 'ae-filter-safe02': ("{% noautoescape %}{{ a }} -- {{ a|safe }}{% endnoautoescape %}", {"a": "<b>hello</b>"}, "<b>hello</b> -- <b>hello</b>"), 153 154 'ae-filter-removetags01': ('{{ a|removetags:"a b" }} {{ b|removetags:"a b" }}', {"a": "<a>x</a> <p><b>y</b></p>", "b": mark_safe("<a>x</a> <p><b>y</b></p>")}, "x <p>y</p> x <p>y</p>"), 155 156 'ae-filter-striptags01': ('{{ a|striptags }} {{ b|striptags }}', {"a": "<a>x</a> <p><b>y</b></p>", "b": mark_safe("<a>x</a> <p><b>y</b></p>")}, "x y x y"), 157 158 'ae-filter-first01': ('{{ a|first }} {{ b|first }}', {"a": ["a&b", "x"], "b": [mark_safe("a&b"), "x"]}, "a&b a&b"), 159 160 'ae-filter-random01': ('{{ a|random }} {{ b|random }}', {"a": ["a&b", "a&b"], "b": [mark_safe("a&b"), mark_safe("a&b")]}, "a&b a&b"), 161 162 'ae-filter-slice01': ('{{ a|slice:"1:3" }} {{ b|slice:"1:3" }}', {"a": "a&b", "b": mark_safe("a&b")}, "&b &b"), 163 164 'ae-filter-unordered_list01': ('{{ a|unordered_list }}', {"a": ["x>", [["<y", []]]]}, "\t<li>x>\n\t<ul>\n\t\t<li><y</li>\n\t</ul>\n\t</li>"), 165 'ae-filter-unordered_list02': ('{{ a|unordered_list }}', {"a": ["x>", [[mark_safe("<y"), []]]]}, "\t<li>x>\n\t<ul>\n\t\t<li><y</li>\n\t</ul>\n\t</li>"), 166 'ae-filter-unordered_list03': ('{% noautoescape %}{{ a|unordered_list }}{% endnoautoescape %}', {"a": ["x>", [["<y", []]]]}, "\t<li>x>\n\t<ul>\n\t\t<li><y</li>\n\t</ul>\n\t</li>"), 167 168 # If the input to "default" filter is marked as safe, then so is the 169 # output. However, if the default arg is used, auto-escaping kicks in (if 170 # enabled), because we cannot mark the default as safe. 171 # Note: we have to use {"a": ""} here, otherwise the invalid template 172 # variable string interferes with the test result. 173 'ae-filter-default01': ('{{ a|default:"x<" }}', {"a": ""}, "x<"), 174 'ae-filter-default02': ('{{ a|default:"x<" }}', {"a": mark_safe("x>")}, "x>"), 175 176 'ae-filter-default_if_none01': ('{{ a|default:"x<" }}', {"a": None}, "x<"), 177 178 'ae-filter-phone2numeric01': ('{{ a|phone2numeric }} {{ b|phone2numeric }}', {"a": "<1-800-call-me>", "b": mark_safe("<1-800-call-me>") }, "<1-800-2255-63> <1-800-2255-63>"), 179 180 # Chaining a bunch of safeness-preserving filters should not alter the safe 181 # status either way. 182 'ae-chaining01': ('{{ a|capfirst|center:"7" }}.{{ b|capfirst|center:"7" }}', {"a": "a < b", "b": mark_safe("a < b")}, " A < b . A < b "), 183 184 # Using a filter that forces a string back to unsafe: 185 'ae-chaining02': ('{{ a|cut:"b"|capfirst }}.{{ b|cut:"b"|capfirst }}', {"a": "a < b", "b": mark_safe("a < b")}, "A < .A < "), 186 187 # Using a filter that forces safeness does not lead to double-escaping 188 'ae-chaining03': ('{{ a|escape|capfirst }}', {"a": "a < b"}, "A < b"), 189 190 # Force to safe, then back (also showing why using force_escape too early 191 # in a chain can lead to unexpected results). 192 'ae-chaining04': ('{{ a|force_escape|cut:"b" }}', {"a": "a < b"}, "a &lt; "), 193 'ae-chaining05': ('{{ a|cut:"b"|force_escape }}', {"a": "a < b"}, "a < "), 194 'ae-chaining06': ('{{ a|cut:"b"|safe }}', {"a": "a < b"}, "a < "), 195 'ae-chaining07': ('{{ a|safe|force_escape }}', {"a": "a < b"}, "a < b"), 196 197 # Escaped string as argument 198 'filter-syntax10': (r'{{ var|default_if_none:" endquote\" hah" }}', {"var": None}, ' endquote" hah'), 199 200 }) 201 202 return TEMPLATE_TESTS 203 204 205 206 207 #def test_template_loader(template_name, template_dirs=None): 208 #"A custom template loader that loads the unit-test templates." 209 #try: 210 #return (TEMPLATE_TESTS[template_name][0] , "test:%s" % template_name) 211 #except KeyError: 212 #raise template.TemplateDoesNotExist, template_name 213 214 #def run_tests(verbosity=0, standalone=False): 215 ## Register our custom template loader. 216 #old_template_loaders = loader.template_source_loaders 217 #loader.template_source_loaders = [test_template_loader] 218 219 #failed_tests = [] 220 #tests = TEMPLATE_TESTS.items() 221 #tests.sort() 222 223 ## Turn TEMPLATE_DEBUG off, because tests assume that. 224 #old_td, settings.TEMPLATE_DEBUG = settings.TEMPLATE_DEBUG, False 225 ## Set TEMPLATE_STRING_IF_INVALID to a known string 226 #old_invalid, settings.TEMPLATE_STRING_IF_INVALID = settings.TEMPLATE_STRING_IF_INVALID, 'INVALID' 227 228 #for name, vals in tests: 229 #install() 230 #if 'LANGUAGE_CODE' in vals[1]: 231 #activate(vals[1]['LANGUAGE_CODE']) 232 #else: 233 #activate('en-us') 234 #try: 235 #ctxt = template.Context(vals[1]) 236 ## Hack for testing: force autoescaping to be in effect. 237 #ctxt.autoescape = True 238 #output = loader.get_template(name).render(ctxt) 239 #except Exception, e: 240 #if e.__class__ == vals[2]: 241 #if verbosity: 242 #print "Template test: %s -- Passed" % name 243 #else: 244 #if verbosity: 245 #traceback.print_exc() 246 #print "Template test: %s -- FAILED. Got %s, exception: %s" % (name, e.__class__, e) 247 #failed_tests.append(name) 248 #continue 249 #if 'LANGUAGE_CODE' in vals[1]: 250 #deactivate() 251 #if output == vals[2]: 252 #if verbosity: 253 #print "Template test: %s -- Passed" % name 254 #else: 255 #if verbosity: 256 #print "Template test: %s -- FAILED. Expected %r, got %r" % (name, vals[2], output) 257 #failed_tests.append(name) 258 #loader.template_source_loaders = old_template_loaders 259 #deactivate() 260 #settings.TEMPLATE_DEBUG = old_td 261 #settings.TEMPLATE_STRING_IF_INVALID = old_invalid 262 263 #if failed_tests and not standalone: 264 #msg = "Template tests %s failed." % failed_tests 265 #if not verbosity: 266 #msg += " Re-run tests with -v1 to see actual failures" 267 #raise Exception, msg 268 269 if __name__ == "__main__": 270 unittest.main() -
tests/regressiontests/defaultfilters/tests.py
diff --git a/tests/regressiontests/defaultfilters/tests.py b/tests/regressiontests/defaultfilters/tests.py index d2335dd87b727dd68bf17dc970596eb104d75938..47e8e24ddd76e41d5e408c7b29cebd17c9d22c96 100644
a b u'a stri to be maled' 168 168 >>> cut(u'a string to be mangled', 'strings') 169 169 u'a string to be mangled' 170 170 171 >>> escape(u'<some html & special characters > here')171 >>> force_escape(u'<some html & special characters > here') 172 172 u'<some html & special characters > here' 173 173 174 >>> escape(u'<some html & special characters > here ĐÅ€£')174 >>> force_escape(u'<some html & special characters > here ĐÅ€£') 175 175 u'<some html & special characters > here \xc4\x90\xc3\x85\xe2\x82\xac\xc2\xa3' 176 176 177 177 >>> linebreaks(u'line 1') -
tests/regressiontests/templates/tests.py
diff --git a/tests/regressiontests/templates/tests.py b/tests/regressiontests/templates/tests.py index 141e5124099c198f99dd2d6de75ffd841349b0cb..1f9552e1cd03c8c119997e3408ea3033cdfe93e6 100644
a b class UTF8Class: 76 76 77 77 class Templates(unittest.TestCase): 78 78 def test_templates(self): 79 TEMPLATE_TESTS = self.get_template_tests() 80 81 # Register our custom template loader. 82 def test_template_loader(template_name, template_dirs=None): 83 "A custom template loader that loads the unit-test templates." 84 try: 85 return (TEMPLATE_TESTS[template_name][0] , "test:%s" % template_name) 86 except KeyError: 87 raise template.TemplateDoesNotExist, template_name 88 89 old_template_loaders = loader.template_source_loaders 90 loader.template_source_loaders = [test_template_loader] 91 92 failures = [] 93 tests = TEMPLATE_TESTS.items() 94 tests.sort() 95 96 # Turn TEMPLATE_DEBUG off, because tests assume that. 97 old_td, settings.TEMPLATE_DEBUG = settings.TEMPLATE_DEBUG, False 98 99 # Set TEMPLATE_STRING_IF_INVALID to a known string 100 old_invalid = settings.TEMPLATE_STRING_IF_INVALID 101 expected_invalid_str = 'INVALID' 102 103 for name, vals in tests: 104 install() 105 106 if isinstance(vals[2], tuple): 107 normal_string_result = vals[2][0] 108 invalid_string_result = vals[2][1] 109 if '%s' in invalid_string_result: 110 expected_invalid_str = 'INVALID %s' 111 invalid_string_result = invalid_string_result % vals[2][2] 112 template.invalid_var_format_string = True 113 else: 114 normal_string_result = vals[2] 115 invalid_string_result = vals[2] 116 117 if 'LANGUAGE_CODE' in vals[1]: 118 activate(vals[1]['LANGUAGE_CODE']) 119 else: 120 activate('en-us') 121 122 for invalid_str, result in [('', normal_string_result), 123 (expected_invalid_str, invalid_string_result)]: 124 settings.TEMPLATE_STRING_IF_INVALID = invalid_str 125 try: 126 test_template = loader.get_template(name) 127 output = self.render(test_template, vals) 128 except Exception, e: 129 if e.__class__ != result: 130 failures.append("Template test (TEMPLATE_STRING_IF_INVALID='%s'): %s -- FAILED. Got %s, exception: %s" % (invalid_str, name, e.__class__, e)) 131 continue 132 if output != result: 133 failures.append("Template test (TEMPLATE_STRING_IF_INVALID='%s'): %s -- FAILED. Expected %r, got %r" % (invalid_str, name, result, output)) 134 135 if 'LANGUAGE_CODE' in vals[1]: 136 deactivate() 137 138 if template.invalid_var_format_string: 139 expected_invalid_str = 'INVALID' 140 template.invalid_var_format_string = False 141 142 loader.template_source_loaders = old_template_loaders 143 deactivate() 144 settings.TEMPLATE_DEBUG = old_td 145 settings.TEMPLATE_STRING_IF_INVALID = old_invalid 146 147 self.assertEqual(failures, [], '\n'.join(failures)) 148 149 def render(self, test_template, vals): 150 return test_template.render(template.Context(vals[1])) 151 152 def get_template_tests(self): 79 153 # NOW and NOW_tz are used by timesince tag tests. 80 154 NOW = datetime.now() 81 155 NOW_tz = datetime.now(LocalTimezone(datetime.now())) 82 156 83 157 # SYNTAX -- 84 158 # 'template_name': ('template contents', 'context dict', 'expected string output' or Exception class) 85 TEMPLATE_TESTS = { 86 159 return { 87 160 ### BASIC SYNTAX ########################################################## 88 161 89 162 # Plain text should go through the template parser untouched … … class Templates(unittest.TestCase): 725 798 'url-fail03' : ('{% url regressiontests.templates.views.client no_such_param="value" %}', {}, ''), 726 799 } 727 800 728 # Register our custom template loader.729 def test_template_loader(template_name, template_dirs=None):730 "A custom template loader that loads the unit-test templates."731 try:732 return (TEMPLATE_TESTS[template_name][0] , "test:%s" % template_name)733 except KeyError:734 raise template.TemplateDoesNotExist, template_name735 736 old_template_loaders = loader.template_source_loaders737 loader.template_source_loaders = [test_template_loader]738 739 failures = []740 tests = TEMPLATE_TESTS.items()741 tests.sort()742 743 # Turn TEMPLATE_DEBUG off, because tests assume that.744 old_td, settings.TEMPLATE_DEBUG = settings.TEMPLATE_DEBUG, False745 746 # Set TEMPLATE_STRING_IF_INVALID to a known string747 old_invalid = settings.TEMPLATE_STRING_IF_INVALID748 expected_invalid_str = 'INVALID'749 750 for name, vals in tests:751 install()752 753 if isinstance(vals[2], tuple):754 normal_string_result = vals[2][0]755 invalid_string_result = vals[2][1]756 if '%s' in invalid_string_result:757 expected_invalid_str = 'INVALID %s'758 invalid_string_result = invalid_string_result % vals[2][2]759 template.invalid_var_format_string = True760 else:761 normal_string_result = vals[2]762 invalid_string_result = vals[2]763 764 if 'LANGUAGE_CODE' in vals[1]:765 activate(vals[1]['LANGUAGE_CODE'])766 else:767 activate('en-us')768 769 for invalid_str, result in [('', normal_string_result),770 (expected_invalid_str, invalid_string_result)]:771 settings.TEMPLATE_STRING_IF_INVALID = invalid_str772 try:773 output = loader.get_template(name).render(template.Context(vals[1]))774 except Exception, e:775 if e.__class__ != result:776 failures.append("Template test (TEMPLATE_STRING_IF_INVALID='%s'): %s -- FAILED. Got %s, exception: %s" % (invalid_str, name, e.__class__, e))777 continue778 if output != result:779 failures.append("Template test (TEMPLATE_STRING_IF_INVALID='%s'): %s -- FAILED. Expected %r, got %r" % (invalid_str, name, result, output))780 781 if 'LANGUAGE_CODE' in vals[1]:782 deactivate()783 784 if template.invalid_var_format_string:785 expected_invalid_str = 'INVALID'786 template.invalid_var_format_string = False787 788 loader.template_source_loaders = old_template_loaders789 deactivate()790 settings.TEMPLATE_DEBUG = old_td791 settings.TEMPLATE_STRING_IF_INVALID = old_invalid792 793 self.assertEqual(failures, [], '\n'.join(failures))794 795 801 if __name__ == "__main__": 796 802 unittest.main()