Ticket #2359: autoescape-1.diff
File autoescape-1.diff, 73.0 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: Wed Jul 18 11:19:35 2007 +0200 Subject: [PATCH] autoescape 1 autoescape-1: Conflicts (especially, a missing import of force_unicode in django/utils/html.py Merged patch autoescape-1. (Base: 90c47c66000a56360a69641f6f3e4f9e96d29114) (Last: 147ff090a7d3d71a6e4ccf91e737f2ae7d108350) --- django/oldforms/__init__.py | 43 ++-- django/template/__init__.py | 26 ++ django/template/context.py | 4 django/template/defaultfilters.py | 131 ++++++++++-- django/template/defaulttags.py | 41 ++++ django/utils/encoding.py | 2 django/utils/html.py | 19 +- 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 | 269 +++++++++++++++++++++++++ tests/regressiontests/defaultfilters/tests.py | 4 tests/regressiontests/templates/tests.py | 144 +++++++------ 15 files changed, 922 insertions(+), 121 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 ea19ca060b42b6214085d019681cfba21b7b9b37 last 35e6d247f711b3224d045c1f8668ed5d3302950c diff --git a/django/oldforms/__init__.py b/django/oldforms/__init__.py index a0f14cdcf2b202dfe264b2cd5417575f0c97002e..30892c5cfc29dead47c82ff4d35111242aa32f75 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, force_unicode … … 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(FormFieldWrapper): 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(SelectMultipleField): 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 6880fd59972819ee5572641fa08ca2cbc0933a29..30a75fc3bcbc956289eb1a77a2de1a42c1a148ff 100644
a b from django.utils.functional import curry, Promise 62 62 from django.utils.text import smart_split 63 63 from django.utils.encoding import smart_unicode, 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): 600 602 arg_vals.append(arg) 601 603 else: 602 604 arg_vals.append(resolve_variable(arg, context)) 603 obj = func(obj, *arg_vals) 605 if getattr(func, 'needs_autoescape', False): 606 new_obj = func(obj, autoescape = context.autoescape, *arg_vals) 607 else: 608 new_obj = func(obj, *arg_vals) 609 if getattr(func, 'is_safe', False) and isinstance(obj, SafeData): 610 obj = mark_safe(new_obj) 611 elif isinstance(obj, EscapeData): 612 obj = mark_for_escaping(new_obj) 613 else: 614 obj = new_obj 615 604 616 return obj 605 617 606 618 def args_check(name, func, provided): … … class VariableNode(Node): 782 794 return "<Variable Node: %s>" % self.filter_expression 783 795 784 796 def render(self, context): 785 return self.filter_expression.resolve(context) 797 output = self.filter_expression.resolve(context) 798 if (context.autoescape and not isinstance(output, SafeData)) or isinstance(output, EscapeData): 799 return escape(output) 800 else: 801 return output 786 802 787 803 class DebugVariableNode(VariableNode): 788 804 def render(self, context): 789 805 try: 790 returnself.filter_expression.resolve(context)806 output = self.filter_expression.resolve(context) 791 807 except TemplateSyntaxError, e: 792 808 if not hasattr(e, 'source'): 793 809 e.source = self.source 794 810 raise 811 if context.autoescape and not isinstance(output, SafeData): 812 return escape(output) 813 else: 814 return output 795 815 796 816 def generic_tag_compiler(params, defaults, name, node_class, parser, token): 797 817 "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 6779b9b5f2b16b2961818a6230ff6b1caf3bb6e4..8e24f67287a883fa3297fc11f2a5fd3f31fba822 100644
a b from django.template import resolve_variable, Library 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 """ … … def slugify(value): 122 135 import unicodedata 123 136 value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore') 124 137 value = unicode(re.sub('[^\w\s-]', '', value).strip().lower()) 125 return re.sub('[-\s]+', '-', value)138 return mark_safe(re.sub('[-\s]+', '-', value)) 126 139 slugify = stringfilter(slugify) 140 slugify.is_safe = True 127 141 128 142 def stringformat(value, arg): 129 143 """ … … def stringformat(value, arg): 138 152 return (u"%" + unicode(arg)) % value 139 153 except (ValueError, TypeError): 140 154 return u"" 155 stringformat.is_safe = True 141 156 142 157 def title(value): 143 158 "Converts a string into titlecase" 144 159 return re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title()) 145 160 title = stringfilter(title) 161 title.is_safe = False 146 162 147 163 def truncatewords(value, arg): 148 164 """ … … def truncatewords(value, arg): 157 173 return value # Fail silently. 158 174 return truncate_words(value, length) 159 175 truncatewords = stringfilter(truncatewords) 176 truncatewords.is_safe = True 160 177 161 178 def truncatewords_html(value, arg): 162 179 """ … … def upper(value): 176 193 "Converts a string into all uppercase" 177 194 return value.upper() 178 195 upper = stringfilter(upper) 196 upper.is_safe = False 179 197 180 198 def urlencode(value): 181 199 "Escapes a value for use in a URL" 182 200 from django.utils.http import urlquote 183 201 return urlquote(value) 184 202 urlencode = stringfilter(urlencode) 203 urlencode.is_safe = False 185 204 186 205 def urlize(value): 187 206 "Converts URLs in plain text into clickable links" 188 207 from django.utils.html import urlize 189 return urlize(value, nofollow=True)208 return mark_safe(urlize(value, nofollow=True)) 190 209 urlize = stringfilter(urlize) 210 urlize.is_safe = True 191 211 192 212 def urlizetrunc(value, limit): 193 213 """ … … def urlizetrunc(value, limit): 197 217 Argument: Length to truncate URLs to. 198 218 """ 199 219 from django.utils.html import urlize 200 return urlize(value, trim_url_limit=int(limit), nofollow=True)220 return mark_safe(urlize(value, trim_url_limit=int(limit), nofollow=True)) 201 221 urlizetrunc = stringfilter(urlizetrunc) 222 urlize.is_safe = True 202 223 203 224 def wordcount(value): 204 225 "Returns the number of words" 205 226 return len(value.split()) 206 227 wordcount = stringfilter(wordcount) 228 wordcount.is_safe = False 207 229 208 230 def wordwrap(value, arg): 209 231 """ … … def wordwrap(value, arg): 214 236 from django.utils.text import wrap 215 237 return wrap(value, int(arg)) 216 238 wordwrap = stringfilter(wordwrap) 239 wordwrap.is_safe = True 217 240 218 241 def ljust(value, arg): 219 242 """ … … def ljust(value, arg): 223 246 """ 224 247 return value.ljust(int(arg)) 225 248 ljust = stringfilter(ljust) 249 ljust.is_safe = True 226 250 227 251 def rjust(value, arg): 228 252 """ … … def rjust(value, arg): 232 256 """ 233 257 return value.rjust(int(arg)) 234 258 rjust = stringfilter(rjust) 259 rjust.is_safe = True 235 260 236 261 def center(value, arg): 237 262 "Centers the value in a field of a given width" 238 263 return value.center(int(arg)) 239 264 center = stringfilter(center) 265 center.is_safe = True 240 266 241 267 def cut(value, arg): 242 268 "Removes all values of arg from the given string" 243 269 return value.replace(arg, u'') 244 270 cut = stringfilter(cut) 271 cut.is_safe = False 245 272 246 273 ################### 247 274 # HTML STRINGS # 248 275 ################### 249 276 250 277 def escape(value): 251 "Escapes a string's HTML" 278 "Marks the value as a string that should not be auto-escaped." 279 from django.utils.safestring import mark_for_escaping 280 return mark_for_escaping(value) 281 escape = stringfilter(escape) 282 escape.is_safe = True 283 284 def force_escape(value): 285 """Escapes a string's HTML. This returns a new string containing the escaped 286 characters (as opposed to "escape", which marks the content for later 287 possible escaping).""" 252 288 from django.utils.html import escape 253 return escape(value)289 return mark_safe(escape(value)) 254 290 escape = stringfilter(escape) 291 force_escape.is_safe = True 255 292 256 def linebreaks(value ):293 def linebreaks(value, autoescape = None): 257 294 "Converts newlines into <p> and <br />s" 258 295 from django.utils.html import linebreaks 259 return linebreaks(value) 296 autoescape = autoescape and not isinstance(value, SafeData) 297 return mark_safe(linebreaks(value, autoescape)) 260 298 linebreaks = stringfilter(linebreaks) 299 linebreaks.is_safe = True 300 linebreaks.needs_autoescape = True 261 301 262 def linebreaksbr(value ):302 def linebreaksbr(value, autoescape = None): 263 303 "Converts newlines into <br />s" 264 return value.replace('\n', '<br />') 304 if autoescape and not isinstance(value, SafeData): 305 from django.utils.html import escape 306 data = escape(value) 307 else: 308 data = value 309 return mark_safe(data.replace('\n', '<br />')) 265 310 linebreaksbr = stringfilter(linebreaksbr) 311 linebreaksbr.is_safe = True 312 linebreaksbr.needs_autoescape = True 313 314 def safe(value): 315 "Marks the value as a string that should not be auto-escaped." 316 from django.utils.safestring import mark_safe 317 return mark_safe(value) 318 safe = stringfilter(safe) 319 safe.is_safe = True 266 320 267 321 def removetags(value, tags): 268 322 "Removes a space separated list of [X]HTML tags from the output" … … def removetags(value, tags): 274 328 value = endtag_re.sub(u'', value) 275 329 return value 276 330 removetags = stringfilter(removetags) 331 removetags.is_safe = True 277 332 278 333 def striptags(value): 279 334 "Strips all [X]HTML tags" 280 335 from django.utils.html import strip_tags 281 336 return strip_tags(value) 282 337 striptags = stringfilter(striptags) 338 striptags.is_safe = True 283 339 284 340 ################### 285 341 # LISTS # … … def dictsort(value, arg): 293 349 decorated = [(resolve_variable(u'var.' + arg, {u'var' : item}), item) for item in value] 294 350 decorated.sort() 295 351 return [item[1] for item in decorated] 352 dictsort.is_safe = False 296 353 297 354 def dictsortreversed(value, arg): 298 355 """ … … def dictsortreversed(value, arg): 303 360 decorated.sort() 304 361 decorated.reverse() 305 362 return [item[1] for item in decorated] 363 dictsortreversed.is_safe = False 306 364 307 365 def first(value): 308 366 "Returns the first item in a list" … … def first(value): 310 368 return value[0] 311 369 except IndexError: 312 370 return u'' 371 first.is_safe = True 313 372 314 373 def join(value, arg): 315 374 "Joins a list with a string, like Python's ``str.join(list)``" 316 375 try: 317 returnarg.join(map(force_unicode, value))376 data = arg.join(map(force_unicode, value)) 318 377 except AttributeError: # fail silently but nicely 319 378 return value 379 safe_args = reduce(lambda lhs, rhs: lhs and isinstance(rhs, SafeData), value, True) 380 if safe_args: 381 return mark_safe(data) 382 else: 383 return data 384 join.is_safe = True 320 385 321 386 def length(value): 322 387 "Returns the length of the value - useful for lists" 323 388 return len(value) 389 length.is_safe = False 324 390 325 391 def length_is(value, arg): 326 392 "Returns a boolean of whether the value's length is the argument" 327 393 return len(value) == int(arg) 394 length.is_safe = False 328 395 329 396 def random(value): 330 397 "Returns a random item from the list" 331 398 return random_module.choice(value) 399 length.is_safe = True 332 400 333 401 def slice_(value, arg): 334 402 """ … … def slice_(value, arg): 349 417 350 418 except (ValueError, TypeError): 351 419 return value # Fail silently. 420 slice_.is_safe = True 352 421 353 def unordered_list(value ):422 def unordered_list(value, autoescape = None): 354 423 """ 355 424 Recursively takes a self-nested list and returns an HTML unordered list -- 356 425 WITHOUT opening and closing <ul> tags. … … def unordered_list(value): 371 440 </ul> 372 441 </li> 373 442 """ 443 if autoescape: 444 from django.utils.html import conditional_escape 445 escaper = conditional_escape 446 else: 447 escaper = lambda x: x 448 374 449 def _helper(value, tabs): 375 450 indent = u'\t' * tabs 376 451 if value[1]: 377 return u'%s<li>%s\n%s<ul>\n%s\n%s</ul>\n%s</li>' % (indent, force_unicode(value[0]), indent,452 return u'%s<li>%s\n%s<ul>\n%s\n%s</ul>\n%s</li>' % (indent, escaper(force_unicode(value[0])), indent, 378 453 u'\n'.join([_helper(v, tabs+1) for v in value[1]]), indent, indent) 379 454 else: 380 return u'%s<li>%s</li>' % (indent, force_unicode(value[0])) 381 return _helper(value, 1) 455 return u'%s<li>%s</li>' % (indent, escaper(force_unicode(value[0]))) 456 return mark_safe(_helper(value, 1)) 457 unordered_list.is_safe = True 458 unordered_list.needs_autoescape = True 382 459 383 460 ################### 384 461 # INTEGERS # … … def unordered_list(value): 387 464 def add(value, arg): 388 465 "Adds the arg to the value" 389 466 return int(value) + int(arg) 467 add.is_safe = False 390 468 391 469 def get_digit(value, arg): 392 470 """ … … def get_digit(value, arg): 406 484 return int(str(value)[-arg]) 407 485 except IndexError: 408 486 return 0 487 get_digit.is_safe = False 409 488 410 489 ################### 411 490 # DATES # … … def date(value, arg=None): 419 498 if arg is None: 420 499 arg = settings.DATE_FORMAT 421 500 return format(value, arg) 501 date.is_safe = False 422 502 423 503 def time(value, arg=None): 424 504 "Formats a time according to the given format" … … def time(value, arg=None): 428 508 if arg is None: 429 509 arg = settings.TIME_FORMAT 430 510 return time_format(value, arg) 511 time.is_safe = False 431 512 432 513 def timesince(value, arg=None): 433 514 'Formats a date as the time since that date (i.e. "4 days, 6 hours")' … … def timesince(value, arg=None): 437 518 if arg: 438 519 return timesince(arg, value) 439 520 return timesince(value) 521 timesince.is_safe = False 440 522 441 523 def timeuntil(value, arg=None): 442 524 'Formats a date as the time until that date (i.e. "4 days, 6 hours")' … … def timeuntil(value, arg=None): 447 529 if arg: 448 530 return timesince(arg, value) 449 531 return timesince(datetime.now(), value) 532 timeuntil.is_safe = False 450 533 451 534 ################### 452 535 # LOGIC # … … def timeuntil(value, arg=None): 455 538 def default(value, arg): 456 539 "If value is unavailable, use given default" 457 540 return value or arg 541 default.is_safe = False 458 542 459 543 def default_if_none(value, arg): 460 544 "If value is None, use given default" 461 545 if value is None: 462 546 return arg 463 547 return value 548 default_if_none.is_safe = False 464 549 465 550 def divisibleby(value, arg): 466 551 "Returns true if the value is devisible by the argument" 467 552 return int(value) % int(arg) == 0 553 divisibleby.is_safe = False 468 554 469 555 def yesno(value, arg=None): 470 556 """ … … def yesno(value, arg=None): 495 581 if value: 496 582 return yes 497 583 return no 584 yesno.is_safe = False 498 585 499 586 ################### 500 587 # MISC # … … def filesizeformat(bytes): 517 604 if bytes < 1024 * 1024 * 1024: 518 605 return ugettext("%.1f MB") % (bytes / (1024 * 1024)) 519 606 return ugettext("%.1f GB") % (bytes / (1024 * 1024 * 1024)) 607 filesizeformat.is_safe = True 520 608 521 609 def pluralize(value, arg=u's'): 522 610 """ … … def pluralize(value, arg=u's'): 544 632 except TypeError: # len() of unsized object 545 633 pass 546 634 return singular_suffix 635 pluralize.is_safe = False 547 636 548 637 def phone2numeric(value): 549 638 "Takes a phone number and converts it in to its numerical equivalent" 550 639 from django.utils.text import phone2numeric 551 640 return phone2numeric(value) 641 phone2numeric.is_safe = True 552 642 553 643 def pprint(value): 554 644 "A wrapper around pprint.pprint -- for debugging, really" … … def pprint(value): 557 647 return pformat(value) 558 648 except Exception, e: 559 649 return u"Error in formatting: %s" % force_unicode(e, errors="replace") 650 pprint.is_safe = True 560 651 561 652 # Syntax: register.filter(name of filter, callback) 562 653 register.filter(add) … … register.filter(filesizeformat) 575 666 register.filter(first) 576 667 register.filter(fix_ampersands) 577 668 register.filter(floatformat) 669 register.filter(force_escape) 578 670 register.filter(get_digit) 579 671 register.filter(iriencode) 580 672 register.filter(join) … … register.filter(pprint) 592 684 register.filter(removetags) 593 685 register.filter(random) 594 686 register.filter(rjust) 687 register.filter(safe) 595 688 register.filter('slice', slice_) 596 689 register.filter(slugify) 597 690 register.filter(stringformat) -
django/template/defaulttags.py
diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py index 7b3a261a5b55b8875c3445913c4dfac4b1f594d0..b4b4270c45c11f4c1f39d9b66312ac82c296493f 100644
a b from django.template import get_library, Library, InvalidTemplateLibrary 6 6 from django.conf import settings 7 7 from django.utils.encoding import smart_str, smart_unicode 8 8 from django.utils.itercompat import groupby 9 from django.utils.safestring import mark_safe 9 10 import sys 10 11 import re 11 12 … … except NameError: 16 17 17 18 register = Library() 18 19 20 class AutoEscapeControlNode(Node): 21 """Implements the actions of both the autoescape and noautescape tags.""" 22 def __init__(self, setting, nodelist): 23 self.setting, self.nodelist = setting, nodelist 24 25 def render(self, context): 26 old_setting = context.autoescape 27 context.autoescape = self.setting 28 output = self.nodelist.render(context) 29 context.autoescape = old_setting 30 if self.setting: 31 return mark_safe(output) 32 else: 33 return output 34 19 35 class CommentNode(Node): 20 36 def render(self, context): 21 37 return '' … … class WithNode(Node): 386 402 return output 387 403 388 404 #@register.tag 405 def autoescape(parser, token): 406 """ 407 Force autoescape behaviour for this block. 408 """ 409 nodelist = parser.parse(('endautoescape',)) 410 parser.delete_first_token() 411 return AutoEscapeControlNode(True, nodelist) 412 autoescape = register.tag(autoescape) 413 414 #@register.tag 389 415 def comment(parser, token): 390 416 """ 391 417 Ignore everything between ``{% comment %}`` and ``{% endcomment %}`` … … def do_filter(parser, token): 488 514 489 515 Sample usage:: 490 516 491 {% filter escape|lower %}517 {% filter force_escape|lower %} 492 518 This text will be HTML-escaped, and will appear in lowercase. 493 519 {% endfilter %} 494 520 """ 495 521 _, rest = token.contents.split(None, 1) 496 522 filter_expr = parser.compile_filter("var|%s" % (rest)) 523 for func, unused in filter_expr.filters: 524 if getattr(func, '_decorated_function', func).__name__ in ('escape', 'safe'): 525 raise TemplateSyntaxError('"filter %s" is not permitted. Use the "autoescape" tag instead.' % func.__name__) 497 526 nodelist = parser.parse(('endfilter',)) 498 527 parser.delete_first_token() 499 528 return FilterNode(filter_expr, nodelist) … … def ifchanged(parser, token): 746 775 ifchanged = register.tag(ifchanged) 747 776 748 777 #@register.tag 778 def noautoescape(parser, token): 779 """ 780 Force autoescape behaviour to be disabled for this block. 781 """ 782 nodelist = parser.parse(('endnoautoescape',)) 783 parser.delete_first_token() 784 return AutoEscapeControlNode(False, nodelist) 785 autoescape = register.tag(noautoescape) 786 787 #@register.tag 749 788 def ssi(parser, token): 750 789 """ 751 790 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 7515d0c41b5dcf5a477963b7fba39e3529ff8e90..a0db1f2a6973b26d5f0f92c96d725a4b76e29cd0 100644
a b def force_unicode(s, encoding='utf-8', strings_only=False, errors='strict'): 39 39 else: 40 40 s = unicode(str(s), encoding, errors) 41 41 elif not isinstance(s, unicode): 42 s = unicode(s,encoding, errors)42 s = s.decode(encoding, errors) 43 43 return s 44 44 45 45 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 ebd04d1b3caf45fdcda4a5fe2782d1d3ed5a6235..281184d28f7fd28116deeaf94c639f231abbdd1c 100644
a b 3 3 import re 4 4 import string 5 5 6 from django.utils.safestring import SafeData 6 7 from django.utils.encoding import force_unicode 7 8 from django.utils.functional import allow_lazy 8 9 … … def escape(html): 30 31 return force_unicode(html).replace('&', '&').replace('<', '<').replace('>', '>').replace('"', '"').replace("'", ''') 31 32 escape = allow_lazy(escape, unicode) 32 33 33 def linebreaks(value): 34 "Convert newlines into <p> and <br />s." 34 def conditional_escape(html): 35 "Similar to escape(), except that it does not operate on pre-escaped strings" 36 if isinstance(html, SafeData): 37 return html 38 else: 39 return escape(html) 40 41 def linebreaks(value, autoescape = False): 42 "Converts newlines into <p> and <br />s" 35 43 value = re.sub(r'\r\n|\r|\n', '\n', force_unicode(value)) # normalize newlines 36 44 paras = re.split('\n{2,}', value) 37 paras = [u'<p>%s</p>' % p.strip().replace('\n', '<br />') for p in paras] 45 if autoescape: 46 paras = [u'<p>%s</p>' % escape(p.strip()).replace('\n', '<br />') for p in paras] 47 else: 48 paras = [u'<p>%s</p>' % p.strip().replace('\n', '<br />') for p in paras] 38 49 return u'\n\n'.join(paras) 39 linebreaks = allow_lazy(linebreaks, unicode) 50 linebreaks = allow_lazy(linebreaks, unicode) 40 51 41 52 def strip_tags(value): 42 53 "Return the given HTML with all tags stripped." -
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 0d53c281d156c0db34a1c573149cb803527d7d81..dbce6cac71c539936adc168c2f19f987da81fb4e 100644
a b it also defines the content that fills the hole in the *parent*. If there were 277 277 two similarly-named ``{% block %}`` tags in a template, that template's parent 278 278 wouldn't know which one of the blocks' content to use. 279 279 280 Automatic HTML escaping 281 ======================= 282 283 A very real problem when creating HTML (and other) output using templates and 284 variable substitution is the possibility of accidently inserting some variable 285 value that affects the resulting HTML. For example, a template fragment like 286 287 :: 288 289 Hello, {{ name }}. 290 291 seems like a harmless way to display the user's name. However, if you are 292 displaying data that the user entered directly and they entered their name as 293 294 :: 295 296 <script>alert('hello')</script> 297 298 this would always display a Javascript alert box whenever the page was loaded. 299 Similarly, if you were displaying some data generated by another process and 300 it contained a '<' symbol, you couldn't just dump this straight into your 301 HTML, because it would be treated as the start of an element. The effects of 302 these sorts of problems can vary from merely annoying to allowing exploits via 303 `Cross Site Scripting`_ (XSS) attacks. 304 305 .. _Cross Site Scripting: http://en.wikipedia.org/wiki/Cross-site_scripting 306 307 In order to provide some protection against these problems, Django provides an 308 auto-escaping template tag. Inside this tag, any data that comes from template 309 variables is examined to see if it contains one of the five HTML characters 310 (<, >, ', " and &) that often need escaping and those characters are converted 311 to their respective HTML entities. 312 313 Because some variables will contain data that is *intended* to be rendered 314 as HTML, template tag and filter writers can mark their output strings as 315 requiring no further escaping. For example, the ``unordered_list`` filter is 316 designed to return raw HTML and we want the template processor to simply 317 display the results as returned, without applying any escaping. That is taken 318 care of by the filter. The template author need do nothing special in that 319 case. 320 321 By default, auto-escaping is not in effect. To enable it inside your template, 322 wrap the affected content in the ``autoescape`` tag, like so:: 323 324 {% autoescape %} 325 Hello {{ name }} 326 {% endautoescape %} 327 328 Since the auto-escaping tag passes its effect onto templates that extend the 329 current one as well as templates included via the ``include`` tag (just like 330 all block tags), if you wrap your main HTML content in an ``autoescape`` tag, 331 you will have automatic escaping applied to all of your content. 332 333 At times, you might want to disable auto-escaping when it would otherwise be 334 in effect. You can do this with the ``noautoescape`` tag. For example:: 335 336 {% autoescape %} 337 Hello {{ name }} 338 339 {% noautoescape %} 340 This will not be auto-escaped: {{ data }}. 341 342 Nor this: {{ other_data }} 343 {% endnoautoescape %} 344 {% endautoescape %} 345 346 For individual variables, the ``safe`` filter can also be used. 347 348 Generally, you will not need to worry about auto-escaping very much. Enable it 349 in your base template once you are entering the main HTML region and then 350 write your templates normally. The view developers and custom filter authors 351 need to think about when their data should not be escaped and mark it 352 appropriately. They are in a better position to know when that should happen 353 than the template author, so it is their responsibility. By default, when 354 auto-escaping is enabled, all output is escaped unless the template processor 355 is explicitly told otherwise. 356 280 357 Using the built-in reference 281 358 ============================ 282 359 … … available, and what they do. 352 429 Built-in tag reference 353 430 ---------------------- 354 431 432 autoescape 433 ~~~~~~~~~~ 434 435 All variables that are output inside this tag have HTML escaping applied to 436 them, as if they each had the ``escape`` filter attached to them. 437 438 The only exceptions are variables that are already marked as 'safe' from 439 escaping, either by the code that populated the variable, or because it has 440 the ``safe`` filter applied. 441 355 442 block 356 443 ~~~~~ 357 444 … … just like in variable syntax. 419 506 420 507 Sample usage:: 421 508 422 {% filter escape|lower %}509 {% filter force_escape|lower %} 423 510 This text will be HTML-escaped, and will appear in all lowercase. 424 511 {% endfilter %} 425 512 … … Load a custom template tag set. 652 739 653 740 See `Custom tag and filter libraries`_ for more information. 654 741 742 noautoescape 743 ~~~~~~~~~~~~ 744 745 Disable the effects of the ``autoescape`` tag (if it is in effect). 746 655 747 now 656 748 ~~~ 657 749 … … Escapes a string's HTML. Specifically, it makes these replacements: 1040 1132 * ``'"'`` (double quote) to ``'"'`` 1041 1133 * ``"'"`` (single quote) to ``'''`` 1042 1134 1135 The escaping is only applied when the string is output, so it does not matter 1136 where in a chained sequence of filters you put ``escape``: it will always be 1137 applied as though it were the last filter. If you want escaping to be applied 1138 immediately, use the ``force_escape`` filter. 1139 1043 1140 filesizeformat 1044 1141 ~~~~~~~~~~~~~~ 1045 1142 … … For example: 1082 1179 Using ``floatformat`` with no argument is equivalent to using ``floatformat`` with 1083 1180 an argument of ``-1``. 1084 1181 1182 force_escape 1183 ~~~~~~~~~~~~ 1184 1185 **New in Django development version** 1186 1187 Applies HTML escaping to a string (see the ``escape`` filter for details). 1188 This filter is applied immediately and returns a new, escaped string. This is 1189 useful in the typically rare cases where you need multiple escaping or want to 1190 apply other filters to the escaped results. Normally, you want to use the 1191 ``escape`` filter. 1192 1085 1193 get_digit 1086 1194 ~~~~~~~~~ 1087 1195 … … Right-aligns the value in a field of a given width. 1203 1311 1204 1312 **Argument:** field size 1205 1313 1314 safe 1315 ~~~~ 1316 1317 Marks a string as not requiring further HTML escaping prior to output. This is 1318 only useful inside an ``autoescape`` block, when the output would otherwise be 1319 automatically escaped. Outside of an ``autoescape`` block, this filter has no 1320 effect. 1321 1206 1322 slice 1207 1323 ~~~~~ 1208 1324 -
docs/templates_python.txt
diff --git a/docs/templates_python.txt b/docs/templates_python.txt index 117656762f9cf5d96b0818722e5929ab4f8cbe94..5b3973b879e0e3970d8b6bd28dad515d1a954309 100644
a b an object to it's string value before being passed to your function:: 680 680 def lower(value): 681 681 return value.lower() 682 682 683 Filters and auto-escaping 684 ~~~~~~~~~~~~~~~~~~~~~~~~~ 685 686 When you are writing a custom filter, you need to give some thought to how 687 this filter will work when rendered in an auto-escaping environment (inside 688 an ``autoescape`` template tag block). First, you should realise that there 689 are three types of strings that can be passed around inside the template code: 690 691 * raw strings are the native Python ``str`` (or ``unicode``) types. On 692 output, they are escaped if they are inside an ``autoescape`` block. 693 * "safe" strings are strings that are safe from further escaping at output 694 time. Any necessary escaping has already been done. They are commonly used 695 for output that contains raw HTML that is intended to be intrepreted on the 696 client side. 697 698 Internally, these strings are of type ``SafeString`` or ``SafeUnicode``, 699 although they share a common base class in ``SafeData``, so you can test 700 for them using code like:: 701 702 if isinstance(value, SafeData): 703 # Do something with the "safe" string. 704 705 * strings which are marked as "need escaping" are *always* escaped on 706 output, regardless of whether they are in an ``autoescape`` block or not. 707 These strings are only escaped once, however, even if used inside an 708 ``autoescaep`` block. This type of string is internally represented by the 709 types ``EscapeString`` and ``EscapeUnicode``. You will not normally need to 710 worry about these; they exist only for the implementation of the ``escape`` 711 filter. 712 713 Inside your filter, you will need to think about three areas in order to be 714 auto-escaping compliant: 715 716 1. If your filter returns a string that is ready for direct output (it should 717 be considered a "safe" string), you should call 718 ``django.utils.safestring.mark_safe()`` on the result prior to returning. 719 This will turn the result into the appropriate ``SafeData`` type. 720 721 2. If your filter is given a "safe" string, is it guaranteed to return a 722 "safe" string? If so, set the ``is_safe`` attribute on the function to be 723 ``True``. For example, a filter that replaced all numbers with the number 724 spelt out in words is going to be safe-string-preserving, since it cannot 725 introduce any of the five dangerous characters: <, >, ", ' or &. So we can 726 write:: 727 728 @register.filter 729 def convert_to_words(value): 730 # ... implementation here ... 731 return result 732 733 convert_to_words.is_safe = True 734 735 Note that this filter does not return a universally safe result (it does not 736 return ``mark_safe(result)``) because if it is handed a raw string such as 737 '<a>', this will need further escaping in an auto-escape environment. The 738 ``is_safe`` attribute only talks about the safeness of the result when a safe 739 string is passed in to the filter. 740 741 3. Will your filter behave differently depending upon whether auto-escaping 742 is currently in effect or not? For example, the ``ordered_list`` filter that 743 ships with Django needs to know whether to escape its content or not. It will 744 always return a safe string, since it returns raw HTML, so we cannot apply 745 escaping to the result -- it needs to be done in-situ. 746 747 For these cases, the filter function needs to be told what the current 748 auto-escaping setting is. Set the ``needs_autoescape`` attribute on the 749 filter to ``True`` and have your function take an extra argument called 750 ``autoescape`` with a default value of ``None``. When the filter is called, 751 the ``autoescape`` keyword argument will be ``True`` if auto-escaping is in 752 effect. For example, the ``unordered_list`` filter is written as:: 753 754 def unordered_list(value, autoescape = None): 755 # ... lots of code here ... 756 757 return mark_safe(...) 758 759 unordered_list.is_safe = True 760 unordered_list.needs_autoescape = True 761 762 By default, both the ``is_safe`` and ``needs_autoescape`` attributes are 763 ``False``. You do not need to specify them if ``False`` is an acceptable 764 value. 765 766 As a matter of convention, we leave ``is_safe`` as ``False`` for filters that 767 do not accept string inputs (they might take a number as an input, for 768 example) or for those that return a non-string (e.g. the ``length`` filter). 769 However, not following this convention will not cause any harm or make your 770 results any more vulnerable to cross-site scripting problems. 771 683 772 Writing custom template tags 684 773 ---------------------------- 685 774 … … Ultimately, this decoupling of compilation and rendering results in an 798 887 efficient template system, because a template can render multiple context 799 888 without having to be parsed multiple times. 800 889 890 Auto-escaping considerations 891 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 892 893 The output from template tags is not automatically run through the 894 auto-escaping filters if used inside an ``autoescape`` tag. However, there are 895 still a couple of things you should keep in mind when writing a template tag: 896 897 If the ``render()`` function of your template stores the result in a context 898 variable (rather than returning the result in a string), it should take care 899 to call ``mark_safe()`` if appropriate. When the variable is ultimately 900 rendered, it will be affected by the auto-escape setting in effect at the 901 time, so content that should be safe from further escaping needs to be marked 902 as such. 903 904 Also, if your template tag creates a new context for performing some 905 sub-rendering, you should be careful to set the auto-escape variable to the 906 current context's value. For example:: 907 908 def render(self, context): 909 # ... 910 new_context = Context({'var': obj}) 911 new_context.autoescape = context.autoescape 912 # ... Do something with new_context ... 913 914 This is not a very common situation, but it is sometimes useful (see 915 ``django.templates.defaulttags.FilterNode.render()`` for an example). 916 801 917 Registering the tag 802 918 ~~~~~~~~~~~~~~~~~~~ 803 919 -
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..3361ae8fd4910650bee3137c21941ba8c30241c6
- + 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:"8" }} {{ b|urlizetrunc:"8" }}', {"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 'filter-syntax10': (r'{{ var|default_if_none:" endquote\" hah" }}', {"var": 198 None}, ' endquote" hah'), 199 }) 200 201 return TEMPLATE_TESTS 202 203 204 205 206 #def test_template_loader(template_name, template_dirs=None): 207 #"A custom template loader that loads the unit-test templates." 208 #try: 209 #return (TEMPLATE_TESTS[template_name][0] , "test:%s" % template_name) 210 #except KeyError: 211 #raise template.TemplateDoesNotExist, template_name 212 213 #def run_tests(verbosity=0, standalone=False): 214 ## Register our custom template loader. 215 #old_template_loaders = loader.template_source_loaders 216 #loader.template_source_loaders = [test_template_loader] 217 218 #failed_tests = [] 219 #tests = TEMPLATE_TESTS.items() 220 #tests.sort() 221 222 ## Turn TEMPLATE_DEBUG off, because tests assume that. 223 #old_td, settings.TEMPLATE_DEBUG = settings.TEMPLATE_DEBUG, False 224 ## Set TEMPLATE_STRING_IF_INVALID to a known string 225 #old_invalid, settings.TEMPLATE_STRING_IF_INVALID = settings.TEMPLATE_STRING_IF_INVALID, 'INVALID' 226 227 #for name, vals in tests: 228 #install() 229 #if 'LANGUAGE_CODE' in vals[1]: 230 #activate(vals[1]['LANGUAGE_CODE']) 231 #else: 232 #activate('en-us') 233 #try: 234 #ctxt = template.Context(vals[1]) 235 ## Hack for testing: force autoescaping to be in effect. 236 #ctxt.autoescape = True 237 #output = loader.get_template(name).render(ctxt) 238 #except Exception, e: 239 #if e.__class__ == vals[2]: 240 #if verbosity: 241 #print "Template test: %s -- Passed" % name 242 #else: 243 #if verbosity: 244 #traceback.print_exc() 245 #print "Template test: %s -- FAILED. Got %s, exception: %s" % (name, e.__class__, e) 246 #failed_tests.append(name) 247 #continue 248 #if 'LANGUAGE_CODE' in vals[1]: 249 #deactivate() 250 #if output == vals[2]: 251 #if verbosity: 252 #print "Template test: %s -- Passed" % name 253 #else: 254 #if verbosity: 255 #print "Template test: %s -- FAILED. Expected %r, got %r" % (name, vals[2], output) 256 #failed_tests.append(name) 257 #loader.template_source_loaders = old_template_loaders 258 #deactivate() 259 #settings.TEMPLATE_DEBUG = old_td 260 #settings.TEMPLATE_STRING_IF_INVALID = old_invalid 261 262 #if failed_tests and not standalone: 263 #msg = "Template tests %s failed." % failed_tests 264 #if not verbosity: 265 #msg += " Re-run tests with -v1 to see actual failures" 266 #raise Exception, msg 267 268 if __name__ == "__main__": 269 unittest.main() -
tests/regressiontests/defaultfilters/tests.py
diff --git a/tests/regressiontests/defaultfilters/tests.py b/tests/regressiontests/defaultfilters/tests.py index a1efae66f66a0826cfc91318ec129ecc03504af6..a792a44f785433834ae4828f3b2485b071a441d7 100644
a b u'a stri to be maled' 190 190 >>> cut(u'a string to be mangled', 'strings') 191 191 u'a string to be mangled' 192 192 193 >>> escape(u'<some html & special characters > here')193 >>> force_escape(u'<some html & special characters > here') 194 194 u'<some html & special characters > here' 195 195 196 >>> escape(u'<some html & special characters > here ĐÅ€£')196 >>> force_escape(u'<some html & special characters > here ĐÅ€£') 197 197 u'<some html & special characters > here \xc4\x90\xc3\x85\xe2\x82\xac\xc2\xa3' 198 198 199 199 >>> linebreaks(u'line 1') -
tests/regressiontests/templates/tests.py
diff --git a/tests/regressiontests/templates/tests.py b/tests/regressiontests/templates/tests.py index 143d1ddaa4f842a4e35bfad0d00ae9191aa85c06..70118c66207dce05744370f08ef15a019c8ac7c6 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): 744 817 'url-fail03' : ('{% url regressiontests.templates.views.client no_such_param="value" %}', {}, ''), 745 818 } 746 819 747 # Register our custom template loader.748 def test_template_loader(template_name, template_dirs=None):749 "A custom template loader that loads the unit-test templates."750 try:751 return (TEMPLATE_TESTS[template_name][0] , "test:%s" % template_name)752 except KeyError:753 raise template.TemplateDoesNotExist, template_name754 755 old_template_loaders = loader.template_source_loaders756 loader.template_source_loaders = [test_template_loader]757 758 failures = []759 tests = TEMPLATE_TESTS.items()760 tests.sort()761 762 # Turn TEMPLATE_DEBUG off, because tests assume that.763 old_td, settings.TEMPLATE_DEBUG = settings.TEMPLATE_DEBUG, False764 765 # Set TEMPLATE_STRING_IF_INVALID to a known string766 old_invalid = settings.TEMPLATE_STRING_IF_INVALID767 expected_invalid_str = 'INVALID'768 769 for name, vals in tests:770 install()771 772 if isinstance(vals[2], tuple):773 normal_string_result = vals[2][0]774 invalid_string_result = vals[2][1]775 if '%s' in invalid_string_result:776 expected_invalid_str = 'INVALID %s'777 invalid_string_result = invalid_string_result % vals[2][2]778 template.invalid_var_format_string = True779 else:780 normal_string_result = vals[2]781 invalid_string_result = vals[2]782 783 if 'LANGUAGE_CODE' in vals[1]:784 activate(vals[1]['LANGUAGE_CODE'])785 else:786 activate('en-us')787 788 for invalid_str, result in [('', normal_string_result),789 (expected_invalid_str, invalid_string_result)]:790 settings.TEMPLATE_STRING_IF_INVALID = invalid_str791 try:792 output = loader.get_template(name).render(template.Context(vals[1]))793 except Exception, e:794 if e.__class__ != result:795 failures.append("Template test (TEMPLATE_STRING_IF_INVALID='%s'): %s -- FAILED. Got %s, exception: %s" % (invalid_str, name, e.__class__, e))796 continue797 if output != result:798 failures.append("Template test (TEMPLATE_STRING_IF_INVALID='%s'): %s -- FAILED. Expected %r, got %r" % (invalid_str, name, result, output))799 800 if 'LANGUAGE_CODE' in vals[1]:801 deactivate()802 803 if template.invalid_var_format_string:804 expected_invalid_str = 'INVALID'805 template.invalid_var_format_string = False806 807 loader.template_source_loaders = old_template_loaders808 deactivate()809 settings.TEMPLATE_DEBUG = old_td810 settings.TEMPLATE_STRING_IF_INVALID = old_invalid811 812 self.assertEqual(failures, [], '\n'.join(failures))813 814 820 if __name__ == "__main__": 815 821 unittest.main()