Ticket #2359: 01-core-changes.2.diff

File 01-core-changes.2.diff, 69.7 KB (added by Michael Radziej <mir@…>, 17 years ago)

updated patch

  • django/oldforms/__init__.py

    From nobody Mon Sep 17 00:00:00 2001
    From: Michael Radziej <mir@noris.de>
    Date: Tue Feb 6 12:56:18 2007 +0100
    Subject: [PATCH] autoescape 1
    
    SafeString needs to explicitly return self for __str__()
    
    ---
    
     django/oldforms/__init__.py                   |   43 ++--
     django/template/__init__.py                   |   26 ++
     django/template/context.py                    |    4 
     django/template/defaultfilters.py             |  129 ++++++++++--
     django/template/defaulttags.py                |   49 ++++-
     django/utils/html.py                          |   15 +
     django/utils/safestring.py                    |  109 ++++++++++
     docs/templates.txt                            |  118 +++++++++++
     docs/templates_python.txt                     |  116 +++++++++++
     tests/regressiontests/autoescape/tests.py     |  266 +++++++++++++++++++++++++
     tests/regressiontests/defaultfilters/tests.py |    2 
     tests/regressiontests/templates/tests.py      |  126 ++++++------
     12 files changed, 893 insertions(+), 110 deletions(-)
     create mode 100644 django/utils/safestring.py
     create mode 100644 tests/regressiontests/autoescape/tests.py
    
    base 86e42e8abea4d494f2276f082933aa6773e82b88
    last e2bddd7b0de6f643f7702089680ca1c38494f5c7
    diff --git a/django/oldforms/__init__.py b/django/oldforms/__init__.py
    index decf0f7064f01feda7e4c5d1bee84b9d60651dd1..1b3bba139a632faf48ae8e7b87d9872633b531d3 100644
    a b  
    11from django.core import validators
    22from django.core.exceptions import PermissionDenied
    33from django.utils.html import escape
     4from django.utils.safestring import mark_safe
    45from django.conf import settings
    56from django.utils.translation import gettext, ngettext
    67
    class FormFieldWrapper(object):  
    181182
    182183    def html_error_list(self):
    183184        if self.errors():
    184             return '<ul class="errorlist"><li>%s</li></ul>' % '</li><li>'.join([escape(e) for e in self.errors()])
     185            return mark_safe('<ul class="errorlist"><li>%s</li></ul>' % '</li><li>'.join([escape(e) for e in self.errors()]))
    185186        else:
    186             return ''
     187            return mark_safe('')
    187188
    188189    def get_id(self):
    189190        return self.formfield.get_id()
    class FormFieldCollection(FormFieldWrapp  
    215216        return bool(len(self.errors()))
    216217
    217218    def html_combined_error_list(self):
    218         return ''.join([field.html_error_list() for field in self.formfield_dict.values() if hasattr(field, 'errors')])
     219        return mark_safe(''.join([field.html_error_list() for field in self.formfield_dict.values() if hasattr(field, 'errors')]))
    219220
    220221class InlineObjectCollection(object):
    221222    "An object that acts like a sparse list of form field collections."
    class TextField(FormField):  
    399400            maxlength = 'maxlength="%s" ' % self.maxlength
    400401        if isinstance(data, unicode):
    401402            data = data.encode(settings.DEFAULT_CHARSET)
    402         return '<input type="%s" id="%s" class="v%s%s" name="%s" size="%s" value="%s" %s/>' % \
     403        return mark_safe('<input type="%s" id="%s" class="v%s%s" name="%s" size="%s" value="%s" %s/>' % \
    403404            (self.input_type, self.get_id(), self.__class__.__name__, self.is_required and ' required' or '',
    404             self.field_name, self.length, escape(data), maxlength)
     405            self.field_name, self.length, escape(data), maxlength))
    405406
    406407    def html2python(data):
    407408        return data
    class LargeTextField(TextField):  
    425426            data = ''
    426427        if isinstance(data, unicode):
    427428            data = data.encode(settings.DEFAULT_CHARSET)
    428         return '<textarea id="%s" class="v%s%s" name="%s" rows="%s" cols="%s">%s</textarea>' % \
     429        return mark_safe('<textarea id="%s" class="v%s%s" name="%s" rows="%s" cols="%s">%s</textarea>' % \
    429430            (self.get_id(), self.__class__.__name__, self.is_required and ' required' or '',
    430             self.field_name, self.rows, self.cols, escape(data))
     431            self.field_name, self.rows, self.cols, escape(data)))
    431432
    432433class HiddenField(FormField):
    433434    def __init__(self, field_name, is_required=False, validator_list=None):
    class HiddenField(FormField):  
    436437        self.validator_list = validator_list[:]
    437438
    438439    def render(self, data):
    439         return '<input type="hidden" id="%s" name="%s" value="%s" />' % \
    440             (self.get_id(), self.field_name, escape(data))
     440        return mark_safe('<input type="hidden" id="%s" name="%s" value="%s" />' % \
     441            (self.get_id(), self.field_name, escape(data)))
    441442
    442443class CheckboxField(FormField):
    443444    def __init__(self, field_name, checked_by_default=False, validator_list=None, is_required=False):
    class CheckboxField(FormField):  
    451452        checked_html = ''
    452453        if data or (data is '' and self.checked_by_default):
    453454            checked_html = ' checked="checked"'
    454         return '<input type="checkbox" id="%s" class="v%s" name="%s"%s />' % \
     455        return mark_safe('<input type="checkbox" id="%s" class="v%s" name="%s"%s />' % \
    455456            (self.get_id(), self.__class__.__name__,
    456             self.field_name, checked_html)
     457            self.field_name, checked_html))
    457458
    458459    def html2python(data):
    459460        "Convert value from browser ('on' or '') to a Python boolean"
    class SelectField(FormField):  
    484485                selected_html = ' selected="selected"'
    485486            output.append('    <option value="%s"%s>%s</option>' % (escape(value), selected_html, escape(display_name)))
    486487        output.append('  </select>')
    487         return '\n'.join(output)
     488        return mark_safe('\n'.join(output))
    488489
    489490    def isValidChoice(self, data, form):
    490491        str_data = str(data)
    class RadioSelectField(FormField):  
    537538                output = ['<ul%s>' % (self.ul_class and ' class="%s"' % self.ul_class or '')]
    538539                output.extend(['<li>%s %s</li>' % (d['field'], d['label']) for d in self.datalist])
    539540                output.append('</ul>')
    540                 return ''.join(output)
     541                return mark_safe(''.join(output))
    541542            def __iter__(self):
    542543                for d in self.datalist:
    543544                    yield d
    class RadioSelectField(FormField):  
    552553            datalist.append({
    553554                'value': value,
    554555                'name': display_name,
    555                 'field': '<input type="radio" id="%s" name="%s" value="%s"%s/>' % \
    556                     (self.get_id() + '_' + str(i), self.field_name, value, selected_html),
    557                 'label': '<label for="%s">%s</label>' % \
     556                'field': mark_safe('<input type="radio" id="%s" name="%s" value="%s"%s/>' % \
     557                    (self.get_id() + '_' + str(i), self.field_name, value, selected_html)),
     558                'label': mark_safe('<label for="%s">%s</label>' % \
    558559                    (self.get_id() + '_' + str(i), display_name),
    559             })
     560            )})
    560561        return RadioFieldRenderer(datalist, self.ul_class)
    561562
    562563    def isValidChoice(self, data, form):
    class SelectMultipleField(SelectField):  
    595596                selected_html = ' selected="selected"'
    596597            output.append('    <option value="%s"%s>%s</option>' % (escape(value), selected_html, escape(choice)))
    597598        output.append('  </select>')
    598         return '\n'.join(output)
     599        return mark_safe('\n'.join(output))
    599600
    600601    def isValidChoice(self, field_data, all_data):
    601602        # data is something like ['1', '2', '3']
    class CheckboxSelectMultipleField(Select  
    648649                (self.get_id() + escape(value), self.__class__.__name__, field_name, checked_html,
    649650                self.get_id() + escape(value), choice))
    650651        output.append('</ul>')
    651         return '\n'.join(output)
     652        return mark_safe('\n'.join(output))
    652653
    653654####################
    654655# FILE UPLOADS     #
    class FileUploadField(FormField):  
    669670            raise validators.CriticalValidationError, gettext("The submitted file is empty.")
    670671
    671672    def render(self, data):
    672         return '<input type="file" id="%s" class="v%s" name="%s" />' % \
    673             (self.get_id(), self.__class__.__name__, self.field_name)
     673        return mark_safe('<input type="file" id="%s" class="v%s" name="%s" />' % \
     674            (self.get_id(), self.__class__.__name__, self.field_name))
    674675
    675676    def html2python(data):
    676677        if data is None:
  • django/template/__init__.py

    diff --git a/django/template/__init__.py b/django/template/__init__.py
    index 771880168445cfa207dfe3a5b04f1fa62a3c7266..05678572108396fcd897499487db1da9c49e73f1 100644
    a b from django.conf import settings  
    6060from django.template.context import Context, RequestContext, ContextPopException
    6161from django.utils.functional import curry
    6262from django.utils.text import smart_split
     63from django.utils.safestring import SafeData, EscapeData, mark_safe, mark_for_escaping
     64from django.utils.html import escape
    6365
    6466__all__ = ('Template', 'Context', 'RequestContext', 'compile_string')
    6567
    class FilterExpression(object):  
    568570                    arg_vals.append(arg)
    569571                else:
    570572                    arg_vals.append(resolve_variable(arg, context))
    571             obj = func(obj, *arg_vals)
     573            if getattr(func, 'needs_autoescape', False):
     574                new_obj = func(obj, autoescape = context.autoescape, *arg_vals)
     575            else:
     576                new_obj = func(obj, *arg_vals)
     577            if getattr(func, 'is_safe', False) and isinstance(obj, SafeData):
     578                obj = mark_safe(new_obj)
     579            elif isinstance(obj, EscapeData):
     580                obj = mark_for_escaping(new_obj)
     581            else:
     582                obj = new_obj
     583               
    572584        return obj
    573585
    574586    def args_check(name, func, provided):
    class VariableNode(Node):  
    754766
    755767    def render(self, context):
    756768        output = self.filter_expression.resolve(context)
    757         return self.encode_output(output)
     769        encoded_output = self.encode_output(output)
     770        if (context.autoescape and not isinstance(encoded_output, SafeData)) or isinstance(encoded_output, EscapeData):
     771            return escape(encoded_output)
     772        else:
     773            return encoded_output
    758774
    759775class DebugVariableNode(VariableNode):
    760776    def render(self, context):
    class DebugVariableNode(VariableNode):  
    764780            if not hasattr(e, 'source'):
    765781                e.source = self.source
    766782            raise
    767         return self.encode_output(output)
     783        encoded_output = self.encode_output(output)
     784        if context.autoescape and not isinstance(encoded_output, SafeData):
     785            return escape(encoded_output)
     786        else:
     787            return encoded_output
    768788
    769789def generic_tag_compiler(params, defaults, name, node_class, parser, token):
    770790    "Returns a template.Node subclass."
  • django/template/context.py

    diff --git a/django/template/context.py b/django/template/context.py
    index ba23e95ab7dc4e7ffbd939e869b397fd431f9159..fdd2c919ce2d7bd1afb9844f08ef5c016ebf32a7 100644
    a b class ContextPopException(Exception):  
    99
    1010class Context(object):
    1111    "A stack container for variable context"
     12
     13    autoescape = False
     14
    1215    def __init__(self, dict_=None):
    1316        dict_ = dict_ or {}
    1417        self.dicts = [dict_]
    class RequestContext(Context):  
    9598            processors = tuple(processors)
    9699        for processor in get_standard_processors() + processors:
    97100            self.update(processor(request))
     101
  • django/template/defaultfilters.py

    diff --git a/django/template/defaultfilters.py b/django/template/defaultfilters.py
    index 1d0f78ce12259aa5de58197bfc1b339f3bf25f84..5616f03266502787fe656272263757b3bbcd4aae 100644
    a b  
    33from django.template import resolve_variable, Library
    44from django.conf import settings
    55from django.utils.translation import gettext
     6from django.utils.safestring import mark_safe, SafeData
    67import re
    78import random as random_module
    89
    ###################  
    1617def addslashes(value):
    1718    "Adds slashes - useful for passing strings to JavaScript, for example."
    1819    return value.replace('\\', '\\\\').replace('"', '\\"').replace("'", "\\'")
     20addslashes.is_safe = True
    1921
    2022def capfirst(value):
    2123    "Capitalizes the first character of the value"
    2224    value = str(value)
    2325    return value and value[0].upper() + value[1:]
     26capfirst.is_safe = True
    2427
    2528def fix_ampersands(value):
    2629    "Replaces ampersands with ``&amp;`` entities"
    2730    from django.utils.html import fix_ampersands
    2831    return fix_ampersands(value)
     32fix_ampersands.is_safe = True
    2933
    3034def floatformat(text, arg=-1):
    3135    """
    def floatformat(text, arg=-1):  
    5862        return '%d' % int(f)
    5963    else:
    6064        formatstr = '%%.%df' % abs(d)
    61         return formatstr % f
     65        return mark_safe(formatstr % f)
     66floatformat.is_safe = True
    6267
    63 def linenumbers(value):
     68def linenumbers(value, autoescape = None):
    6469    "Displays text with line numbers"
    6570    from django.utils.html import escape
    6671    lines = value.split('\n')
    6772    # Find the maximum width of the line count, for use with zero padding string format command
    6873    width = str(len(str(len(lines))))
    69     for i, line in enumerate(lines):
    70         lines[i] = ("%0" + width  + "d. %s") % (i + 1, escape(line))
    71     return '\n'.join(lines)
     74    if not autoescape or isinstance(value, SafeData):
     75        for i, line in enumerate(lines):
     76            lines[i] = ("%0" + width  + "d. %s") % (i + 1, line)
     77    else:
     78        for i, line in enumerate(lines):
     79            lines[i] = ("%0" + width  + "d. %s") % (i + 1, escape(line))
     80    return mark_safe('\n'.join(lines))
     81linenumbers.is_safe = True
     82linenumbers.needs_autoescape = True
    7283
    7384def lower(value):
    7485    "Converts a string into all lowercase"
    7586    return value.lower()
     87lower.is_safe = True
    7688
    7789def make_list(value):
    7890    """
    def make_list(value):  
    8092    digits. For a string, it's a list of characters.
    8193    """
    8294    return list(str(value))
     95make_list.is_safe = False
    8396
    8497def slugify(value):
    8598    "Converts to lowercase, removes non-alpha chars and converts spaces to hyphens"
    8699    value = re.sub('[^\w\s-]', '', value).strip().lower()
    87     return re.sub('[-\s]+', '-', value)
     100    return mark_safe(re.sub('[-\s]+', '-', value))
     101slugify.is_safe = True
    88102
    89103def stringformat(value, arg):
    90104    """
    def stringformat(value, arg):  
    99113        return ("%" + arg) % value
    100114    except (ValueError, TypeError):
    101115        return ""
     116stringformat.is_safe = True
    102117
    103118def title(value):
    104119    "Converts a string into titlecase"
    105120    return re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title())
     121title.is_safe = False
    106122
    107123def truncatewords(value, arg):
    108124    """
    def truncatewords(value, arg):  
    118134    if not isinstance(value, basestring):
    119135        value = str(value)
    120136    return truncate_words(value, length)
     137truncatewords.is_safe = True
    121138
    122139def upper(value):
    123140    "Converts a string into all uppercase"
    124141    return value.upper()
     142upper.is_safe = False
    125143
    126144def urlencode(value):
    127145    "Escapes a value for use in a URL"
    128146    import urllib
    129147    return urllib.quote(value)
     148urlencode.is_safe = False
    130149
    131150def urlize(value):
    132151    "Converts URLs in plain text into clickable links"
    133152    from django.utils.html import urlize
    134     return urlize(value, nofollow=True)
     153    return mark_safe(urlize(value, nofollow=True))
     154urlize.is_safe = True
    135155
    136156def urlizetrunc(value, limit):
    137157    """
    def urlizetrunc(value, limit):  
    141161    Argument: Length to truncate URLs to.
    142162    """
    143163    from django.utils.html import urlize
    144     return urlize(value, trim_url_limit=int(limit), nofollow=True)
     164    return mark_safe(urlize(value, trim_url_limit=int(limit), nofollow=True))
     165urlize.is_safe = True
    145166
    146167def wordcount(value):
    147168    "Returns the number of words"
    148169    return len(value.split())
     170wordcount.is_safe = False
    149171
    150172def wordwrap(value, arg):
    151173    """
    def wordwrap(value, arg):  
    155177    """
    156178    from django.utils.text import wrap
    157179    return wrap(str(value), int(arg))
     180wordwrap.is_safe = True
    158181
    159182def ljust(value, arg):
    160183    """
    def ljust(value, arg):  
    163186    Argument: field size
    164187    """
    165188    return str(value).ljust(int(arg))
     189ljust.is_safe = True
    166190
    167191def rjust(value, arg):
    168192    """
    def rjust(value, arg):  
    171195    Argument: field size
    172196    """
    173197    return str(value).rjust(int(arg))
     198rjust.is_safe = True
    174199
    175200def center(value, arg):
    176201    "Centers the value in a field of a given width"
    177202    return str(value).center(int(arg))
     203center.is_safe = True
    178204
    179205def cut(value, arg):
    180206    "Removes all values of arg from the given string"
    181207    return value.replace(arg, '')
     208cut.is_safe = False
    182209
    183210###################
    184211# HTML STRINGS    #
    185212###################
    186213
    187214def escape(value):
    188     "Escapes a string's HTML"
     215    "Marks the value as a string that should not be auto-escaped."
     216    from django.utils.safestring import mark_for_escaping
     217    return mark_for_escaping(value)
     218escape.is_safe = True
     219
     220def force_escape(value):
     221    """Escapes a string's HTML. This returns a new string containing the escaped
     222    characters (as opposed to "escape", which marks the content for later
     223    possible escaping)."""
    189224    from django.utils.html import escape
    190     return escape(value)
     225    return mark_safe(escape(value))
     226force_escape.is_safe = True
    191227
    192 def linebreaks(value):
     228def linebreaks(value, autoescape = None):
    193229    "Converts newlines into <p> and <br />s"
    194230    from django.utils.html import linebreaks
    195     return linebreaks(value)
     231    autoescape = autoescape and not isinstance(value, SafeData)
     232    return mark_safe(linebreaks(value, autoescape))
     233linebreaks.is_safe = True
     234linebreaks.needs_autoescape = True
    196235
    197 def linebreaksbr(value):
     236def linebreaksbr(value, autoescape = None):
    198237    "Converts newlines into <br />s"
    199     return value.replace('\n', '<br />')
     238    if autoescape and not isinstance(value, SafeData):
     239        from django.utils.html import escape
     240        data = escape(value)
     241    else:
     242        data = value
     243    return mark_safe(data.replace('\n', '<br />'))
     244linebreaksbr.is_safe = True
     245linebreaksbr.needs_autoescape = True
     246
     247def safe(value):
     248    "Marks the value as a string that should not be auto-escaped."
     249    from django.utils.safestring import mark_safe
     250    return mark_safe(value)
     251safe.is_safe = True
    200252
    201253def removetags(value, tags):
    202254    "Removes a space separated list of [X]HTML tags from the output"
    def removetags(value, tags):  
    207259    value = starttag_re.sub('', value)
    208260    value = endtag_re.sub('', value)
    209261    return value
     262removetags.is_safe = True
    210263
    211264def striptags(value):
    212265    "Strips all [X]HTML tags"
    def striptags(value):  
    214267    if not isinstance(value, basestring):
    215268        value = str(value)
    216269    return strip_tags(value)
     270striptags.is_safe = True
    217271
    218272###################
    219273# LISTS           #
    def dictsort(value, arg):  
    227281    decorated = [(resolve_variable('var.' + arg, {'var' : item}), item) for item in value]
    228282    decorated.sort()
    229283    return [item[1] for item in decorated]
     284dictsort.is_safe = False
    230285
    231286def dictsortreversed(value, arg):
    232287    """
    def dictsortreversed(value, arg):  
    237292    decorated.sort()
    238293    decorated.reverse()
    239294    return [item[1] for item in decorated]
     295dictsortreversed.is_safe = False
    240296
    241297def first(value):
    242298    "Returns the first item in a list"
    def first(value):  
    244300        return value[0]
    245301    except IndexError:
    246302        return ''
     303first.is_safe = True
    247304
    248305def join(value, arg):
    249306    "Joins a list with a string, like Python's ``str.join(list)``"
    250307    try:
    251         return arg.join(map(str, value))
     308        data = arg.join(map(str, value))
    252309    except AttributeError: # fail silently but nicely
    253310        return value
     311    safe_args = reduce(lambda lhs, rhs: lhs and isinstance(rhs, SafeData), value, True)
     312    if safe_args:
     313        return mark_safe(data)
     314    else:
     315        return data
     316join.is_safe = True
    254317
    255318def length(value):
    256319    "Returns the length of the value - useful for lists"
    257320    return len(value)
     321length.is_safe = False
    258322
    259323def length_is(value, arg):
    260324    "Returns a boolean of whether the value's length is the argument"
    261325    return len(value) == int(arg)
     326length.is_safe = False
    262327
    263328def random(value):
    264329    "Returns a random item from the list"
    265330    return random_module.choice(value)
     331length.is_safe = True
    266332
    267333def slice_(value, arg):
    268334    """
    def slice_(value, arg):  
    283349
    284350    except (ValueError, TypeError):
    285351        return value # Fail silently.
     352slice_.is_safe = True
    286353
    287 def unordered_list(value):
     354def unordered_list(value, autoescape = None):
    288355    """
    289356    Recursively takes a self-nested list and returns an HTML unordered list --
    290357    WITHOUT opening and closing <ul> tags.
    def unordered_list(value):  
    305372        </ul>
    306373        </li>
    307374    """
     375    if autoescape:
     376        from django.utils.html import conditional_escape
     377        escaper = conditional_escape
     378    else:
     379        escaper = lambda x: x
     380
    308381    def _helper(value, tabs):
    309382        indent = '\t' * tabs
    310383        if value[1]:
    311             return '%s<li>%s\n%s<ul>\n%s\n%s</ul>\n%s</li>' % (indent, value[0], indent,
     384            return '%s<li>%s\n%s<ul>\n%s\n%s</ul>\n%s</li>' % (indent, escaper(value[0]), indent,
    312385                '\n'.join([_helper(v, tabs+1) for v in value[1]]), indent, indent)
    313386        else:
    314             return '%s<li>%s</li>' % (indent, value[0])
    315     return _helper(value, 1)
     387            return '%s<li>%s</li>' % (indent, escaper(value[0]))
     388    return mark_safe(_helper(value, 1))
     389unordered_list.is_safe = True
     390unordered_list.needs_autoescape = True
    316391
    317392###################
    318393# INTEGERS        #
    ###################  
    321396def add(value, arg):
    322397    "Adds the arg to the value"
    323398    return int(value) + int(arg)
     399add.is_safe = False
    324400
    325401def get_digit(value, arg):
    326402    """
    def get_digit(value, arg):  
    340416        return int(str(value)[-arg])
    341417    except IndexError:
    342418        return 0
     419get_digit.is_safe = False
    343420
    344421###################
    345422# DATES           #
    def date(value, arg=None):  
    353430    if arg is None:
    354431        arg = settings.DATE_FORMAT
    355432    return format(value, arg)
     433date.is_safe = False
    356434
    357435def time(value, arg=None):
    358436    "Formats a time according to the given format"
    def time(value, arg=None):  
    362440    if arg is None:
    363441        arg = settings.TIME_FORMAT
    364442    return time_format(value, arg)
     443time.is_safe = False
    365444
    366445def timesince(value, arg=None):
    367446    'Formats a date as the time since that date (i.e. "4 days, 6 hours")'
    def timesince(value, arg=None):  
    371450    if arg:
    372451        return timesince(arg, value)
    373452    return timesince(value)
     453timesince.is_safe = False
    374454
    375455def timeuntil(value, arg=None):
    376456    'Formats a date as the time until that date (i.e. "4 days, 6 hours")'
    def timeuntil(value, arg=None):  
    381461    if arg:
    382462        return timesince(arg, value)
    383463    return timesince(datetime.now(), value)
     464timeuntil.is_safe = False
    384465
    385466###################
    386467# LOGIC           #
    ###################  
    389470def default(value, arg):
    390471    "If value is unavailable, use given default"
    391472    return value or arg
     473default.is_safe = False
    392474
    393475def default_if_none(value, arg):
    394476    "If value is None, use given default"
    395477    if value is None:
    396478        return arg
    397479    return value
     480default_if_none.is_safe = False
    398481
    399482def divisibleby(value, arg):
    400483    "Returns true if the value is devisible by the argument"
    401484    return int(value) % int(arg) == 0
     485divisibleby.is_safe = False
    402486
    403487def yesno(value, arg=None):
    404488    """
    def yesno(value, arg=None):  
    429513    if value:
    430514        return yes
    431515    return no
     516yesno.is_safe = False
    432517
    433518###################
    434519# MISC            #
    def filesizeformat(bytes):  
    451536    if bytes < 1024 * 1024 * 1024:
    452537        return "%.1f MB" % (bytes / (1024 * 1024))
    453538    return "%.1f GB" % (bytes / (1024 * 1024 * 1024))
     539filesizeformat.is_safe = True
    454540
    455541def pluralize(value, arg='s'):
    456542    """
    def pluralize(value, arg='s'):  
    478564        except TypeError: # len() of unsized object
    479565            pass
    480566    return singular_suffix
     567pluralize.is_safe = False
    481568
    482569def phone2numeric(value):
    483570    "Takes a phone number and converts it in to its numerical equivalent"
    484571    from django.utils.text import phone2numeric
    485572    return phone2numeric(value)
     573phone2numeric.is_safe = True
    486574
    487575def pprint(value):
    488576    "A wrapper around pprint.pprint -- for debugging, really"
    def pprint(value):  
    491579        return pformat(value)
    492580    except Exception, e:
    493581        return "Error in formatting:%s" % e
     582pprint.is_safe = True
    494583
    495584# Syntax: register.filter(name of filter, callback)
    496585register.filter(add)
    register.filter(filesizeformat)  
    509598register.filter(first)
    510599register.filter(fix_ampersands)
    511600register.filter(floatformat)
     601register.filter(force_escape)
    512602register.filter(get_digit)
    513603register.filter(join)
    514604register.filter(length)
    register.filter(pprint)  
    525615register.filter(removetags)
    526616register.filter(random)
    527617register.filter(rjust)
     618register.filter(safe)
    528619register.filter('slice', slice_)
    529620register.filter(slugify)
    530621register.filter(stringformat)
  • django/template/defaulttags.py

    diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py
    index 3f3f4bda5657e6cc46ed5696410e1441fd33cfcd..9eb4490259af1b8da35abf2268aa850c1ae8406b 100644
    a b from django.template import Node, NodeLi  
    44from django.template import TemplateSyntaxError, VariableDoesNotExist, BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_TAG_END, SINGLE_BRACE_START, SINGLE_BRACE_END, COMMENT_TAG_START, COMMENT_TAG_END
    55from django.template import get_library, Library, InvalidTemplateLibrary
    66from django.conf import settings
     7from django.utils.safestring import mark_safe
    78import sys
    89
    910register = Library()
    1011
     12class AutoEscapeControlNode(Node):
     13    """Implements the actions of both the autoescape and noautescape tags."""
     14    def __init__(self, setting, nodelist):
     15        self.setting, self.nodelist = setting, nodelist
     16
     17    def render(self, context):
     18        old_setting = context.autoescape
     19        context.autoescape = self.setting
     20        output = self.nodelist.render(context)
     21        context.autoescape = old_setting
     22        if self.setting:
     23            return mark_safe(output)
     24        else:
     25            return output
     26
    1127class CommentNode(Node):
    1228    def render(self, context):
    1329        return ''
    class FilterNode(Node):  
    4157    def render(self, context):
    4258        output = self.nodelist.render(context)
    4359        # apply filters
    44         return self.filter_expr.resolve(Context({'var': output}))
     60        ctxt = Context({'var': output})
     61        ctxt.autoescape = context.autoescape
     62        return self.filter_expr.resolve(ctxt)
    4563
    4664class FirstOfNode(Node):
    4765    def __init__(self, vars):
    class RegroupNode(Node):  
    234252            return ''
    235253        output = [] # list of dictionaries in the format {'grouper': 'key', 'list': [list of contents]}
    236254        for obj in obj_list:
    237             grouper = self.expression.resolve(Context({'var': obj}), True)
     255            ctxt = Context({'var': obj})
     256            ctxt.autoescape = context.autoescape
     257            grouper = self.expression.resolve(ctxt, True)
    238258            # TODO: Is this a sensible way to determine equality?
    239259            if output and repr(output[-1]['grouper']) == repr(grouper):
    240260                output[-1]['list'].append(obj)
    class WidthRatioNode(Node):  
    336356        return str(int(round(ratio)))
    337357
    338358#@register.tag
     359def autoescape(parser, token):
     360    """
     361    Force autoescape behaviour for this block.
     362    """
     363    nodelist = parser.parse(('endautoescape',))
     364    parser.delete_first_token()
     365    return AutoEscapeControlNode(True, nodelist)
     366autoescape = register.tag(autoescape)
     367
     368#@register.tag
    339369def comment(parser, token):
    340370    """
    341371    Ignore everything between ``{% comment %}`` and ``{% endcomment %}``
    def do_filter(parser, token):  
    429459
    430460    Sample usage::
    431461
    432         {% filter escape|lower %}
     462        {% filter force_escape|lower %}
    433463            This text will be HTML-escaped, and will appear in lowercase.
    434464        {% endfilter %}
    435465    """
    436466    _, rest = token.contents.split(None, 1)
    437467    filter_expr = parser.compile_filter("var|%s" % (rest))
     468    for func, unused in filter_expr.filters:
     469        if func.__name__ in ('escape', 'safe'):
     470            raise TemplateSyntaxError('"filter %s" is not permitted.  Use the "autoescape" tag instead.' % func.__name__)
    438471    nodelist = parser.parse(('endfilter',))
    439472    parser.delete_first_token()
    440473    return FilterNode(filter_expr, nodelist)
    def ifchanged(parser, token):  
    675708ifchanged = register.tag(ifchanged)
    676709
    677710#@register.tag
     711def noautoescape(parser, token):
     712    """
     713    Force autoescape behaviour to be disabled for this block.
     714    """
     715    nodelist = parser.parse(('endnoautoescape',))
     716    parser.delete_first_token()
     717    return AutoEscapeControlNode(False, nodelist)
     718autoescape = register.tag(noautoescape)
     719
     720#@register.tag
    678721def ssi(parser, token):
    679722    """
    680723    Output the contents of a given file into the page.
  • django/utils/html.py

    diff --git a/django/utils/html.py b/django/utils/html.py
    index a0d1e82dcf007822468822e3374aecd83837b006..2b314e9dabe6e22a0fea4c8020e15a5a7f739caf 100644
    a b  
    11"HTML utilities suitable for global use."
    22
    33import re, string
     4from django.utils.safestring import SafeData
    45
    56# Configuration for urlize() function
    67LEADING_PUNCTUATION  = ['(', '<', '&lt;']
    def escape(html):  
    2728        html = str(html)
    2829    return html.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;').replace('"', '&quot;').replace("'", '&#39;')
    2930
    30 def linebreaks(value):
     31def conditional_escape(html):
     32    "Similar to escape(), except that it does not operate on pre-escaped strings"
     33    if isinstance(html, SafeData):
     34        return html
     35    else:
     36        return escape(html)
     37
     38def linebreaks(value, autoescape = False):
    3139    "Converts newlines into <p> and <br />s"
    3240    value = re.sub(r'\r\n|\r|\n', '\n', value) # normalize newlines
    3341    paras = re.split('\n{2,}', value)
    34     paras = ['<p>%s</p>' % p.strip().replace('\n', '<br />') for p in paras]
     42    if autoescape:
     43        paras = ['<p>%s</p>' % escape(p.strip()).replace('\n', '<br />') for p in paras]
     44    else:
     45        paras = ['<p>%s</p>' % p.strip().replace('\n', '<br />') for p in paras]
    3546    return '\n\n'.join(paras)
    3647
    3748def strip_tags(value):
  • new file django/utils/safestring.py

    diff --git a/django/utils/safestring.py b/django/utils/safestring.py
    new file mode 100644
    index 0000000000000000000000000000000000000000..eac37c686ea6c5ebbedb34c6c3738854a8ad90ff
    - +  
     1"""
     2Functions for working with "safe strings": strings that can be displayed safely
     3without further escaping in HTML. Here, a "safe string" means that the producer
     4of the string has already turned characters that should not be interpreted by
     5the HTML engine (e.g. '<') into the appropriate entities.
     6"""
     7from django.utils.functional import curry
     8
     9class EscapeData(object):
     10    pass
     11
     12class EscapeString(str, EscapeData):
     13    """
     14    A string that should be HTML-escaped when output.
     15    """
     16    pass
     17
     18class EscapeUnicode(unicode, EscapeData):
     19    """
     20    A unicode object that should be HTML-escaped when output.
     21    """
     22    pass
     23
     24class SafeData(object):
     25    pass
     26
     27class SafeString(str, SafeData):
     28    """
     29    A string subclass that has been specifically marked as "safe" for HTML
     30    output purposes.
     31    """
     32    def __add__(self, rhs):
     33        """
     34        Concatenating a safe string with another safe string or safe unicode
     35        object is safe. Otherwise, the result is no longer safe.
     36        """
     37        if isinstance(rhs, SafeUnicode):
     38            return SafeUnicode(self + rhs)
     39        elif isinstance(rhs, SafeString):
     40            return SafeString(self, rhs)
     41        else:
     42            return super(SafeString, self).__add__(rhs)
     43
     44    def __str__(self):
     45        return self
     46
     47class SafeUnicode(unicode, SafeData):
     48    """
     49    A unicode subclass that has been specifically marked as "safe" for HTML
     50    output purposes.
     51    """
     52    def __add__(self, rhs):
     53        """
     54        Concatenating a safe unicode object with another safe string or safe
     55        unicode object is safe. Otherwise, the result is no longer safe.
     56        """
     57        if isinstance(rhs, SafeData):
     58            return SafeUnicode(self + rhs)
     59        else:
     60            return super(SafeUnicode, self).__add__(rhs)
     61
     62    def _proxy_method(self, *args, **kwargs):
     63        """
     64        Wrap a call to a normal unicode method up so that we return safe
     65        results. The method that is being wrapped is passed in the 'method'
     66        argument.
     67        """
     68        method = kwargs.pop('method')
     69        data = method(self, *args, **kwargs)
     70        if isinstance(data, str):
     71            return SafeString(data)
     72        else:
     73            return SafeUnicode(data)
     74
     75    encode = curry(_proxy_method, method = unicode.encode)
     76    decode = curry(_proxy_method, method = unicode.decode)
     77
     78
     79def mark_safe(s):
     80    """
     81    Explicitly mark a string as safe for (HTML) output purposes. The returned
     82    object can be used everywhere a string or unicode object is appropriate.
     83
     84    Can safely be called multiple times on a single string.
     85    """
     86    if isinstance(s, SafeData):
     87        return s
     88    if isinstance(s, str):
     89        return SafeString(s)
     90    if isinstance(s, unicode):
     91        return SafeUnicode(s)
     92    return SafeString(str(s))
     93
     94def mark_for_escaping(s):
     95    """
     96    Explicitly mark a string as requiring HTML escaping upon output. Has no
     97    effect on SafeData subclasses.
     98
     99    Can be safely called multiple times on a single string (the effect is only
     100    applied once).
     101    """
     102    if isinstance(s, SafeData) or isinstance(s, EscapeData):
     103        return s
     104    if isinstance(s, str):
     105        return EscapeString(s)
     106    if isinstance(s, unicode):
     107        return EscapeUnicode(s)
     108    return EscapeString(str(s))
     109
  • docs/templates.txt

    diff --git a/docs/templates.txt b/docs/templates.txt
    index 9f8fe446b499142f0000140e3a1ac4f7dbe947c2..1365bb0593db7b3ac3a03353979db973cbf45528 100644
    a b it also defines the content that fills t  
    260260two similarly-named ``{% block %}`` tags in a template, that template's parent
    261261wouldn't know which one of the blocks' content to use.
    262262
     263Automatic HTML escaping
     264=======================
     265
     266A very real problem when creating HTML (and other) output using templates and
     267variable substitution is the possibility of accidently inserting some variable
     268value that affects the resulting HTML. For example, a template fragment like
     269
     270::
     271
     272    Hello, {{ name }}.
     273
     274seems like a harmless way to display the user's name. However, if you are
     275displaying data that the user entered directly and they entered their name as
     276
     277::
     278
     279    <script>alert('hello')</script>
     280
     281this would always display a Javascript alert box whenever the page was loaded.
     282Similarly, if you were displaying some data generated by another process and
     283it contained a '<' symbol, you couldn't just dump this straight into your
     284HTML, because it would be treated as the start of an element.  The effects of
     285these sorts of problems can vary from merely annoying to allowing exploits via
     286`Cross Site Scripting`_ (XSS) attacks.
     287
     288.. _Cross Site Scripting: http://en.wikipedia.org/wiki/Cross-site_scripting
     289
     290In order to provide some protection against these problems, Django provides an
     291auto-escaping template tag. Inside this tag, any data that comes from template
     292variables is examined to see if it contains one of the five HTML characters
     293(<, >, ', " and &) that often need escaping and those characters are converted
     294to their respective HTML entities.
     295
     296Because some variables will contain data that is *intended* to be rendered
     297as HTML, template tag and filter writers can mark their output strings as
     298requiring no further escaping. For example, the ``unordered_list`` filter is
     299designed to return raw HTML and we want the template processor to simply
     300display the results as returned, without applying any escaping. That is taken
     301care of by the filter. The template author need do nothing special in that
     302case.
     303
     304By default, auto-escaping is not in effect. To enable it inside your template,
     305wrap the affected content in the ``autoescape`` tag, like so::
     306
     307    {% autoescape %}
     308        Hello {{ name }}
     309    {% endautoescape %}
     310
     311Since the auto-escaping tag passes its effect onto templates that extend the
     312current one as well as templates included via the ``include`` tag (just like
     313all block tags), if you wrap your main HTML content in an ``autoescape`` tag,
     314you will have automatic escaping applied to all of your content.
     315
     316At times, you might want to disable auto-escaping when it would otherwise be
     317in effect. You can do this with the ``noautoescape`` tag. For example::
     318
     319    {% autoescape %}
     320        Hello {{ name }}
     321
     322        {% noautoescape %}
     323            This will not be auto-escaped: {{ data }}.
     324
     325            Nor this: {{ other_data }}
     326        {% endnoautoescape %}
     327    {% endautoescape %}
     328
     329For individual variables, the ``safe`` filter can also be used.
     330
     331Generally, you will not need to worry about auto-escaping very much. Enable it
     332in your base template once you are entering the main HTML region and then
     333write your templates normally. The view developers and custom filter authors
     334need to think about when their data should not be escaped and mark it
     335appropriately.  They are in a better position to know when that should happen
     336than the template author, so it is their responsibility. By default, when
     337auto-escaping is enabled, all output is escaped unless the template processor
     338is explicitly told otherwise.
     339
    263340Using the built-in reference
    264341============================
    265342
    available, and what they do.  
    335412Built-in tag reference
    336413----------------------
    337414
     415autoescape
     416~~~~~~~~~~
     417
     418All variables that are output inside this tag have HTML escaping applied to
     419them, as if they each had the ``escape`` filter attached to them.
     420
     421The only exceptions are variables that are already marked as 'safe' from
     422escaping, either by the code that populated the variable, or because it has
     423the ``safe`` filter applied.
     424
    338425block
    339426~~~~~
    340427
    just like in variable syntax.  
    402489
    403490Sample usage::
    404491
    405     {% filter escape|lower %}
     492    {% filter force_escape|lower %}
    406493        This text will be HTML-escaped, and will appear in all lowercase.
    407494    {% endfilter %}
    408495
    Load a custom template tag set.  
    617704
    618705See `Custom tag and filter libraries`_ for more information.
    619706
     707noautoescape
     708~~~~~~~~~~~~
     709
     710Disable the effects of the ``autoescape`` tag (if it is in effect).
     711
    620712now
    621713~~~
    622714
    Escapes a string's HTML. Specifically, i  
    905997    * ``'"'`` (double quote) to ``'&quot;'``
    906998    * ``"'"`` (single quote) to ``'&#39;'``
    907999
     1000The escaping is only applied when the string is output, so it does not matter
     1001where in a chained sequence of filters you put ``escape``: it will always be
     1002applied as though it were the last filter. If you want escaping to be applied
     1003immediately, use the ``force_escape`` filter.
     1004
    9081005filesizeformat
    9091006~~~~~~~~~~~~~~
    9101007
    For example:  
    9491046Using ``floatformat`` with no argument is equivalent to using ``floatformat`` with
    9501047an argument of ``-1``.
    9511048
     1049force_escape
     1050~~~~~~~~~~~~
     1051
     1052**New in Django development version**
     1053
     1054Applies HTML escaping to a string (see the ``escape`` filter for details).
     1055This filter is applied immediately and returns a new, escaped string. This is
     1056useful in the typically rare cases where you need multiple escaping or want to
     1057apply other filters to the escaped results. Normally, you want to use the
     1058``escape`` filter.
     1059
    9521060get_digit
    9531061~~~~~~~~~
    9541062
    Right-aligns the value in a field of a g  
    10601168
    10611169**Argument:** field size
    10621170
     1171safe
     1172~~~~
     1173
     1174Marks a string as not requiring further HTML escaping prior to output. This is
     1175only useful inside an ``autoescape`` block, when the output would otherwise be
     1176automatically escaped. Outside of an ``autoescape`` block, this filter has no
     1177effect.
     1178
    10631179slice
    10641180~~~~~
    10651181
  • docs/templates_python.txt

    diff --git a/docs/templates_python.txt b/docs/templates_python.txt
    index 5f9c5bde4327b142187132226a90a6c960d55b55..68877d444227d561fa0550320ff9faf134867c90 100644
    a b decorator instead::  
    654654If you leave off the ``name`` argument, as in the second example above, Django
    655655will use the function's name as the filter name.
    656656
     657Filters and auto-escaping
     658~~~~~~~~~~~~~~~~~~~~~~~~~
     659
     660When you are writing a custom filter, you need to give some thought to how
     661this filter will work when rendered in an auto-escaping environment (inside
     662an ``autoescape`` template tag block). First, you should realise that there
     663are three types of strings that can be passed around inside the template code:
     664
     665 * raw strings are the native Python ``str`` (or ``unicode``) types. On
     666   output, they are escaped if they are inside an ``autoescape`` block.
     667 * "safe" strings are strings that are safe from further escaping at output
     668   time. Any necessary escaping has already been done. They are commonly used
     669   for output that contains raw HTML that is intended to be intrepreted on the
     670   client side.
     671
     672   Internally, these strings are of type ``SafeString`` or ``SafeUnicode``,
     673   although they share a common base class in ``SafeData``, so you can test
     674   for them using code like::
     675
     676    if isinstance(value, SafeData):
     677        # Do something with the "safe" string.
     678
     679 * strings which are marked as "need escaping" are *always* escaped on
     680   output, regardless of whether they are in an ``autoescape`` block or not.
     681   These strings are only escaped once, however, even if used inside an
     682   ``autoescaep`` block.  This type of string is internally represented by the
     683   types ``EscapeString`` and ``EscapeUnicode``. You will not normally need to
     684   worry about these; they exist only for the implementation of the ``escape``
     685   filter.
     686
     687Inside your filter, you will need to think about three areas in order to be
     688auto-escaping compliant:
     689
     690 1. If your filter returns a string that is ready for direct output (it should
     691 be considered a "safe" string), you should call
     692 ``django.utils.safestring.mark_safe()`` on the result prior to returning.
     693 This will turn the result into the appropriate ``SafeData`` type.
     694
     695 2. If your filter is given a "safe" string, is it guaranteed to return a
     696 "safe" string? If so, set the ``is_safe`` attribute on the function to be
     697 ``True``. For example, a filter that replaced all numbers with the number
     698 spelt out in words is going to be safe-string-preserving, since it cannot
     699 introduce any of the five dangerous characters: <, >, ", ' or &. So we can
     700 write::
     701
     702    @register.filter
     703    def convert_to_words(value):
     704        # ... implementation here ...
     705        return result
     706
     707    convert_to_words.is_safe = True
     708
     709 Note that this filter does not return a universally safe result (it does not
     710 return ``mark_safe(result)``) because if it is handed a raw string such as
     711 '<a>', this will need further escaping in an auto-escape environment. The
     712 ``is_safe`` attribute only talks about the safeness of the result when a safe
     713 string is passed in to the filter.
     714
     715 3. Will your filter behave differently depending upon whether auto-escaping
     716 is currently in effect or not? For example, the ``ordered_list`` filter that
     717 ships with Django needs to know whether to escape its content or not. It will
     718 always return a safe string, since it returns raw HTML, so we cannot apply
     719 escaping to the result -- it needs to be done in-situ.
     720
     721 For these cases, the filter function needs to be told what the current
     722 auto-escaping setting is. Set the ``needs_autoescape`` attribute on the
     723 filter to ``True`` and have your function take an extra argument called
     724 ``autoescape`` with a default value of ``None``. When the filter is called,
     725 the ``autoescape`` keyword argument will be ``True`` if auto-escaping is in
     726 effect. For example, the ``unordered_list`` filter is written as::
     727
     728    def unordered_list(value, autoescape = None):
     729        # ... lots of code here ...
     730
     731        return mark_safe(...)
     732
     733    unordered_list.is_safe = True
     734    unordered_list.needs_autoescape = True
     735
     736By default, both the ``is_safe`` and ``needs_autoescape`` attributes are
     737``False``. You do not need to specify them if ``False`` is an acceptable
     738value.
     739
     740As a matter of convention, we leave ``is_safe`` as ``False`` for filters that
     741do not accept string inputs (they might take a number as an input, for
     742example) or for those that return a non-string (e.g. the ``length`` filter).
     743However, not following this convention will not cause any harm or make your
     744results any more vulnerable to cross-site scripting problems.
     745
    657746Writing custom template tags
    658747----------------------------
    659748
    Ultimately, this decoupling of compilati  
    772861efficient template system, because a template can render multiple context
    773862without having to be parsed multiple times.
    774863
     864Auto-escaping considerations
     865~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     866
     867The output from template tags is not automatically run through the
     868auto-escaping filters if used inside an ``autoescape`` tag. However, there are
     869still a couple of things you should keep in mind when writing a template tag:
     870
     871If the ``render()`` function of your template stores the result in a context
     872variable (rather than returning the result in a string), it should take care
     873to call ``mark_safe()`` if appropriate. When the variable is ultimately
     874rendered, it will be affected by the auto-escape setting in effect at the
     875time, so content that should be safe from further escaping needs to be marked
     876as such.
     877
     878Also, if your template tag creates a new context for performing some
     879sub-rendering, you should be careful to set the auto-escape variable to the
     880current context's value. For example::
     881
     882    def render(self, context):
     883        # ...
     884        new_context = Context({'var': obj})
     885        new_context.autoescape = context.autoescape
     886        # ... Do something with new_context ...
     887
     888This is not a very common situation, but it is sometimes useful (see
     889``django.templates.defaulttags.FilterNode.render()`` for an example).
     890
    775891Registering the tag
    776892~~~~~~~~~~~~~~~~~~~
    777893
  • new file tests/regressiontests/autoescape/tests.py

    diff --git a/tests/regressiontests/autoescape/tests.py b/tests/regressiontests/autoescape/tests.py
    new file mode 100644
    index 0000000000000000000000000000000000000000..22b89e28e6bf032aa6c287f1861adedb3023bb47
    - +  
     1from django.conf import settings
     2
     3if __name__ == '__main__':
     4    # When running this file in isolation, we need to set up the configuration
     5    # before importing 'template'.
     6    settings.configure()
     7
     8from regressiontests.templates.tests import Templates
     9from django import template
     10from django.template import loader
     11from django.utils.translation import activate, deactivate, install
     12from django.utils.tzinfo import LocalTimezone
     13from django.utils.safestring import mark_safe
     14from datetime import datetime, timedelta
     15import unittest
     16
     17class 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&quot; 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>"}, "&lt;b&gt;first&lt;/b&gt;"),
     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>"}, "&lt;b&gt;hello&lt;/b&gt;"),
     53       
     54            # Noautoescape and autoescape nest in a predictable way.
     55            'autoescape-tag04': ("{% noautoescape %}{{ first }} {% autoescape %}{{ first }}{% endautoescape %}{% endnoautoescape %}", {"first": "<a>"}, "<a> &lt;a&gt;"),
     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"&lt;a&gt;\&#39; <a>\'"),
     68       
     69            'ae-filter-capfirst01': ("{{ a|capfirst }} {{ b|capfirst }}", {"a": "fred>", "b": mark_safe("fred&gt;")}, "Fred&gt; Fred&gt;"),
     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;amp;b a&amp;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&lt;two&gt;\nthree")}, "1. one\n2. &lt;two&gt;\n3. three 1. one\n2. &lt;two&gt;\n3. three"),
     80            'ae-filter-linenumbers02': ("{% noautoescape %}{{ a|linenumbers }} {{ b|linenumbers }}{% endnoautoescape %}", {"a": "one\n<two>\nthree", "b": mark_safe("one\n&lt;two&gt;\nthree")}, "1. one\n2. <two>\n3. three 1. one\n2. &lt;two&gt;\n3. three"),
     81       
     82            'ae-filter-lower01': ("{{ a|lower }} {{ b|lower }}", {"a": "Apple & banana", "b": mark_safe("Apple &amp; banana")}, "apple &amp; banana apple &amp; 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("&")}, "[&#39;&amp;&#39;]"),
     87            'ae-filter-make_list02': ('{{ a|make_list|stringformat:"s"|safe }}', {"a": mark_safe("&")}, "['&']"),
     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 &amp; 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&lt;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 &amp; bravo")}, "alpha &amp; ... alpha &amp; ..."),
     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 &amp; b")}, "A &amp; B A &amp;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 &amp; b")}, 'a &amp; 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 &amp; b")}, "3 3"),
     111       
     112            'ae-filter-wordwrap01': ('{{ a|wordwrap:"3" }} {{ b|wordwrap:"3" }}', {"a": "a & b", "b": mark_safe("a & b")}, "a &amp;\nb a &\nb"),
     113       
     114            'ae-filter-ljust01': ('.{{ a|ljust:"5" }}. .{{ b|ljust:"5" }}.', {"a": "a&b", "b": mark_safe("a&b")}, ".a&amp;b  . .a&b  ."),
     115       
     116            'ae-filter-rjust01': ('.{{ a|rjust:"5" }}. .{{ b|rjust:"5" }}.', {"a": "a&b", "b": mark_safe("a&b")}, ".  a&amp;b. .  a&b."),
     117       
     118            'ae-filter-center01': ('.{{ a|center:"5" }}. .{{ b|center:"5" }}.', {"a": "a&b", "b": mark_safe("a&b")}, ". a&amp;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&amp;y")}, "&amp;y &amp;amp;y"),
     123            'ae-filter-cut02': ('{{ a|cut:"&" }} {{ b|cut:"&" }}', {"a": "x&y", "b": mark_safe("x&amp;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&amp;y x&y"),
     128            'ae-filter-escape02': ('{% noautoescape %}{{ a|escape }} {{ b|escape }}{% endnoautoescape %}', {"a": "x&y", "b": mark_safe("x&y")}, "x&amp;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&amp;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&amp;y"),
     137            'ae-filter-force-escape02': ('{{ a|force_escape|force_escape }}', {"a": "x&y"}, "x&amp;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&amp;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&amp;<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&amp;<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>"}, "&lt;b&gt;hello&lt;/b&gt; -- <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 &lt;p&gt;y&lt;/p&gt; 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&amp;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&amp;b a&b"),
     161       
     162            'ae-filter-slice01': ('{{ a|slice:"1:3" }} {{ b|slice:"1:3" }}', {"a": "a&b", "b": mark_safe("a&b")}, "&amp;b &b"),
     163       
     164            'ae-filter-unordered_list01': ('{{ a|unordered_list }}', {"a": ["x>", [["<y", []]]]}, "\t<li>x&gt;\n\t<ul>\n\t\t<li>&lt;y</li>\n\t</ul>\n\t</li>"),
     165            'ae-filter-unordered_list02': ('{{ a|unordered_list }}', {"a": ["x>", [[mark_safe("<y"), []]]]}, "\t<li>x&gt;\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&lt;"),
     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&lt;"),
     177       
     178            'ae-filter-phone2numeric01': ('{{ a|phone2numeric }} {{ b|phone2numeric }}', {"a": "<1-800-call-me>", "b": mark_safe("<1-800-call-me>") }, "&lt;1-800-2255-63&gt; <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 &lt; 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 &lt; .A &lt; "),
     186       
     187            # Using a filter that forces safeness does not lead to double-escaping
     188            'ae-chaining03': ('{{ a|escape|capfirst }}', {"a": "a < b"}, "A &lt; 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 &amp;lt; "),
     193            'ae-chaining05': ('{{ a|cut:"b"|force_escape }}', {"a": "a < b"}, "a &lt; "),
     194            'ae-chaining06': ('{{ a|cut:"b"|safe }}', {"a": "a < b"}, "a < "),
     195            'ae-chaining07': ('{{ a|safe|force_escape }}', {"a": "a < b"}, "a &lt; b"),
     196        })
     197
     198        return TEMPLATE_TESTS
     199
     200
     201   
     202
     203#def test_template_loader(template_name, template_dirs=None):
     204    #"A custom template loader that loads the unit-test templates."
     205    #try:
     206        #return (TEMPLATE_TESTS[template_name][0] , "test:%s" % template_name)
     207    #except KeyError:
     208        #raise template.TemplateDoesNotExist, template_name
     209
     210#def run_tests(verbosity=0, standalone=False):
     211    ## Register our custom template loader.
     212    #old_template_loaders = loader.template_source_loaders
     213    #loader.template_source_loaders = [test_template_loader]
     214
     215    #failed_tests = []
     216    #tests = TEMPLATE_TESTS.items()
     217    #tests.sort()
     218
     219    ## Turn TEMPLATE_DEBUG off, because tests assume that.
     220    #old_td, settings.TEMPLATE_DEBUG = settings.TEMPLATE_DEBUG, False
     221    ## Set TEMPLATE_STRING_IF_INVALID to a known string
     222    #old_invalid, settings.TEMPLATE_STRING_IF_INVALID = settings.TEMPLATE_STRING_IF_INVALID, 'INVALID'
     223   
     224    #for name, vals in tests:
     225        #install()
     226        #if 'LANGUAGE_CODE' in vals[1]:
     227            #activate(vals[1]['LANGUAGE_CODE'])
     228        #else:
     229            #activate('en-us')
     230        #try:
     231            #ctxt = template.Context(vals[1])
     232            ## Hack for testing: force autoescaping to be in effect.
     233            #ctxt.autoescape = True
     234            #output = loader.get_template(name).render(ctxt)
     235        #except Exception, e:
     236            #if e.__class__ == vals[2]:
     237                #if verbosity:
     238                    #print "Template test: %s -- Passed" % name
     239            #else:
     240                #if verbosity:
     241                    #traceback.print_exc()
     242                    #print "Template test: %s -- FAILED. Got %s, exception: %s" % (name, e.__class__, e)
     243                #failed_tests.append(name)
     244            #continue
     245        #if 'LANGUAGE_CODE' in vals[1]:
     246            #deactivate()
     247        #if output == vals[2]:
     248            #if verbosity:
     249                #print "Template test: %s -- Passed" % name
     250        #else:
     251            #if verbosity:
     252                #print "Template test: %s -- FAILED. Expected %r, got %r" % (name, vals[2], output)
     253            #failed_tests.append(name)
     254    #loader.template_source_loaders = old_template_loaders
     255    #deactivate()
     256    #settings.TEMPLATE_DEBUG = old_td
     257    #settings.TEMPLATE_STRING_IF_INVALID = old_invalid
     258
     259    #if failed_tests and not standalone:
     260        #msg = "Template tests %s failed." % failed_tests
     261        #if not verbosity:
     262            #msg += "  Re-run tests with -v1 to see actual failures"
     263        #raise Exception, msg
     264
     265if __name__ == "__main__":
     266    unittest.main()
  • tests/regressiontests/defaultfilters/tests.py

    diff --git a/tests/regressiontests/defaultfilters/tests.py b/tests/regressiontests/defaultfilters/tests.py
    index 439a40c31bc4afcc0f1c55fc2bd85d67d19af91e..75c1afa4dc2f7fa37b26258c2de096edbfd4608a 100644
    a b u'\xcb'  
    141141>>> cut('a string to be mangled', 'strings')
    142142'a string to be mangled'
    143143
    144 >>> escape('<some html & special characters > here')
     144>>> force_escape('<some html & special characters > here')
    145145'&lt;some html &amp; special characters &gt; here'
    146146
    147147>>> linebreaks('line 1')
  • tests/regressiontests/templates/tests.py

    diff --git a/tests/regressiontests/templates/tests.py b/tests/regressiontests/templates/tests.py
    index 0a41f5b5b7b605da059dc5008b9386dfd74ffdb2..c22151012be44abd19d9e19de9e54fa66ba9dd89 100644
    a b class UnicodeInStrClass:  
    7070
    7171class Templates(unittest.TestCase):
    7272    def test_templates(self):
     73        TEMPLATE_TESTS = self.get_template_tests()
     74
     75        # Register our custom template loader.
     76        def test_template_loader(template_name, template_dirs=None):
     77            "A custom template loader that loads the unit-test templates."
     78            try:
     79                return (TEMPLATE_TESTS[template_name][0] , "test:%s" % template_name)
     80            except KeyError:
     81                raise template.TemplateDoesNotExist, template_name
     82
     83        old_template_loaders = loader.template_source_loaders
     84        loader.template_source_loaders = [test_template_loader]
     85
     86        failures = []
     87        tests = TEMPLATE_TESTS.items()
     88        tests.sort()
     89
     90        # Turn TEMPLATE_DEBUG off, because tests assume that.
     91        old_td, settings.TEMPLATE_DEBUG = settings.TEMPLATE_DEBUG, False
     92
     93        # Set TEMPLATE_STRING_IF_INVALID to a known string
     94        old_invalid = settings.TEMPLATE_STRING_IF_INVALID
     95
     96        for name, vals in tests:
     97            install()
     98
     99            if isinstance(vals[2], tuple):
     100                normal_string_result = vals[2][0]
     101                invalid_string_result = vals[2][1]
     102            else:
     103                normal_string_result = vals[2]
     104                invalid_string_result = vals[2]
     105
     106            if 'LANGUAGE_CODE' in vals[1]:
     107                activate(vals[1]['LANGUAGE_CODE'])
     108            else:
     109                activate('en-us')
     110
     111            for invalid_str, result in [('', normal_string_result),
     112                                        ('INVALID', invalid_string_result)]:
     113                settings.TEMPLATE_STRING_IF_INVALID = invalid_str
     114                try:
     115                    test_template = loader.get_template(name)
     116                    output = self.render(test_template, vals)
     117                except Exception, e:
     118                    if e.__class__ != result:
     119                        failures.append("Template test (TEMPLATE_STRING_IF_INVALID='%s'): %s -- FAILED. Got %s, exception: %s" % (invalid_str, name, e.__class__, e))
     120                    continue
     121                if output != result:
     122                    failures.append("Template test (TEMPLATE_STRING_IF_INVALID='%s'): %s -- FAILED. Expected %r, got %r" % (invalid_str, name, result, output))
     123
     124            if 'LANGUAGE_CODE' in vals[1]:
     125                deactivate()
     126
     127        loader.template_source_loaders = old_template_loaders
     128        deactivate()
     129        settings.TEMPLATE_DEBUG = old_td
     130        settings.TEMPLATE_STRING_IF_INVALID = old_invalid
     131
     132        self.assertEqual(failures, [], '\n'.join(failures))
     133
     134    def render(self, test_template, vals):
     135        return test_template.render(template.Context(vals[1]))
     136
     137    def get_template_tests(self):
    73138        # NOW and NOW_tz are used by timesince tag tests.
    74139        NOW = datetime.now()
    75140        NOW_tz = datetime.now(LocalTimezone(datetime.now()))
    76141
    77142        # SYNTAX --
    78143        # 'template_name': ('template contents', 'context dict', 'expected string output' or Exception class)
    79         TEMPLATE_TESTS = {
    80 
     144        return {
    81145            ### BASIC SYNTAX ##########################################################
    82146
    83147            # Plain text should go through the template parser untouched
    class Templates(unittest.TestCase):  
    632696            'timeuntil05' : ('{{ a|timeuntil:b }}', {'a':NOW - timedelta(days=2), 'b':NOW - timedelta(days=2, minutes=1)}, '1 minute'),
    633697        }
    634698
    635         # Register our custom template loader.
    636         def test_template_loader(template_name, template_dirs=None):
    637             "A custom template loader that loads the unit-test templates."
    638             try:
    639                 return (TEMPLATE_TESTS[template_name][0] , "test:%s" % template_name)
    640             except KeyError:
    641                 raise template.TemplateDoesNotExist, template_name
    642 
    643         old_template_loaders = loader.template_source_loaders
    644         loader.template_source_loaders = [test_template_loader]
    645 
    646         failures = []
    647         tests = TEMPLATE_TESTS.items()
    648         tests.sort()
    649 
    650         # Turn TEMPLATE_DEBUG off, because tests assume that.
    651         old_td, settings.TEMPLATE_DEBUG = settings.TEMPLATE_DEBUG, False
    652 
    653         # Set TEMPLATE_STRING_IF_INVALID to a known string
    654         old_invalid = settings.TEMPLATE_STRING_IF_INVALID
    655 
    656         for name, vals in tests:
    657             install()
    658 
    659             if isinstance(vals[2], tuple):
    660                 normal_string_result = vals[2][0]
    661                 invalid_string_result = vals[2][1]
    662             else:
    663                 normal_string_result = vals[2]
    664                 invalid_string_result = vals[2]
    665 
    666             if 'LANGUAGE_CODE' in vals[1]:
    667                 activate(vals[1]['LANGUAGE_CODE'])
    668             else:
    669                 activate('en-us')
    670 
    671             for invalid_str, result in [('', normal_string_result),
    672                                         ('INVALID', invalid_string_result)]:
    673                 settings.TEMPLATE_STRING_IF_INVALID = invalid_str
    674                 try:
    675                     output = loader.get_template(name).render(template.Context(vals[1]))
    676                 except Exception, e:
    677                     if e.__class__ != result:
    678                         failures.append("Template test (TEMPLATE_STRING_IF_INVALID='%s'): %s -- FAILED. Got %s, exception: %s" % (invalid_str, name, e.__class__, e))
    679                     continue
    680                 if output != result:
    681                     failures.append("Template test (TEMPLATE_STRING_IF_INVALID='%s'): %s -- FAILED. Expected %r, got %r" % (invalid_str, name, result, output))
    682 
    683             if 'LANGUAGE_CODE' in vals[1]:
    684                 deactivate()
    685 
    686         loader.template_source_loaders = old_template_loaders
    687         deactivate()
    688         settings.TEMPLATE_DEBUG = old_td
    689         settings.TEMPLATE_STRING_IF_INVALID = old_invalid
    690 
    691         self.assertEqual(failures, [], '\n'.join(failures))
    692 
    693699if __name__ == "__main__":
    694700    unittest.main()
Back to Top