Ticket #2594: ticket-2594-template-strip-leading-whitespace-2012-06-28.diff

File ticket-2594-template-strip-leading-whitespace-2012-06-28.diff, 26.8 KB (added by Leon Matthews, 12 years ago)

Update patch against 1.5-dev

  • AUTHORS

    diff --git a/AUTHORS b/AUTHORS
    index ea0b019..969ba49 100644
    a b answer newbie questions, and generally made Django that much better:  
    292292    Ian G. Kelly <ian.g.kelly@gmail.com>
    293293    Niall Kelly <duke.sam.vimes@gmail.com>
    294294    Ryan Kelly <ryan@rfk.id.au>
     295    Stephen Kelly <steveire@gmail.com>
    295296    Thomas Kerpe <thomas@kerpe.net>
    296297    Wiley Kestner <wiley.kestner@gmail.com>
    297298    Ossama M. Khayat <okhayat@yahoo.com>
  • django/conf/global_settings.py

    diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py
    index 13f7991..05bb413 100644
    a b TEMPLATE_CONTEXT_PROCESSORS = (  
    209209# Output to use in template system for invalid (e.g. misspelled) variables.
    210210TEMPLATE_STRING_IF_INVALID = ''
    211211
     212# Remove the empty lines that had template tags on them.
     213TEMPLATE_STRIP_LEADING_WHITESPACE = False
     214
    212215# Default email address to use for various automated correspondence from
    213216# the site managers.
    214217DEFAULT_FROM_EMAIL = 'webmaster@localhost'
  • django/template/base.py

    diff --git a/django/template/base.py b/django/template/base.py
    index 89bc909..0964f5d 100644
    a b UNKNOWN_SOURCE = '<unknown source>'  
    5555
    5656# match a variable or block tag and capture the entire tag, including start/end
    5757# delimiters
    58 tag_re = (re.compile('(%s.*?%s|%s.*?%s|%s.*?%s)' %
    59           (re.escape(BLOCK_TAG_START), re.escape(BLOCK_TAG_END),
    60            re.escape(VARIABLE_TAG_START), re.escape(VARIABLE_TAG_END),
    61            re.escape(COMMENT_TAG_START), re.escape(COMMENT_TAG_END))))
     58tag_re = re.compile('''
     59    (
     60          %(BLOCK_TAG_START)s.*?%(BLOCK_TAG_END)s        # block start + inner + end
     61        | %(VARIABLE_TAG_START)s.*?%(VARIABLE_TAG_END)s  # variable start + inner + end
     62        | %(COMMENT_TAG_START)s.*?%(COMMENT_TAG_END)s    # comment start + inner + end
     63    )
     64    ''' % {
     65        'BLOCK_TAG_START': re.escape(BLOCK_TAG_START),
     66        'BLOCK_TAG_END': re.escape(BLOCK_TAG_END),
     67        'VARIABLE_TAG_START': re.escape(VARIABLE_TAG_START),
     68        'VARIABLE_TAG_END': re.escape(VARIABLE_TAG_END),
     69        'COMMENT_TAG_START': re.escape(COMMENT_TAG_START),
     70        'COMMENT_TAG_END': re.escape(COMMENT_TAG_END),
     71    }, re.VERBOSE)
     72
     73strip_leading_whitespace_tag_re = re.compile('''
     74    (
     75    [\n\r]{1}     # 1 vertical white space (the one starting this line)
     76    [ \t]*?       # any number of tabs or spaces
     77    (?:           # group but don't match
     78        %(BLOCK_TAG_START)s
     79        [^\n\r%(BLOCK_TAG_START)s%(BLOCK_TAG_END)s]*?       # anything *not* vertical whitespace or
     80        %(BLOCK_TAG_END)s                                   # opening/closing block tag
     81    |
     82        %(COMMENT_TAG_START)s
     83        [^\n\r%(COMMENT_TAG_START)s%(COMMENT_TAG_END)s]*?   # anything *not* vertical whitespace or
     84        %(COMMENT_TAG_END)s                                 # opening/closing comment tag
     85    |
     86        %(VARIABLE_TAG_START)s
     87        [^\n\r%(VARIABLE_TAG_START)s%(VARIABLE_TAG_END)s]*? # anything *not* vertical whitespace or
     88        %(VARIABLE_TAG_END)s                                # opening/closing variable tag
     89    )
     90    (?=                      # Match this only if the previous matched
     91        [ \t]*?
     92        [\n\r]+
     93    )|
     94          %(BLOCK_TAG_START)s.*?%(BLOCK_TAG_END)s           # block start + inner + end
     95        | %(VARIABLE_TAG_START)s.*?%(VARIABLE_TAG_END)s     # variable start + inner + end
     96        | %(COMMENT_TAG_START)s.*?%(COMMENT_TAG_END)s       # comment start + inner + end
     97    )''' % {
     98        'BLOCK_TAG_START': re.escape(BLOCK_TAG_START),
     99        'BLOCK_TAG_END': re.escape(BLOCK_TAG_END),
     100        'VARIABLE_TAG_START': re.escape(VARIABLE_TAG_START),
     101        'VARIABLE_TAG_END': re.escape(VARIABLE_TAG_END),
     102        'COMMENT_TAG_START': re.escape(COMMENT_TAG_START),
     103        'COMMENT_TAG_END': re.escape(COMMENT_TAG_END),
     104    }, re.VERBOSE)
     105
     106# find the command within a block tag
     107block_command_re = re.compile('%s(?P<token_string>.*?)%s' % (re.escape(BLOCK_TAG_START), re.escape(BLOCK_TAG_END)))
     108comment_command_re = re.compile('%s(?P<token_string>.*?)%s' % (re.escape(COMMENT_TAG_START), re.escape(COMMENT_TAG_END)))
     109variable_command_re = re.compile('%s(?P<token_string>.*?)%s' % (re.escape(VARIABLE_TAG_START), re.escape(VARIABLE_TAG_END)))
    62110
    63111# global dictionary of libraries that have been loaded using get_library
    64112libraries = {}
    class Token(object):  
    179227            split.append(bit)
    180228        return split
    181229
     230
    182231class Lexer(object):
    183232    def __init__(self, template_string, origin):
    184233        self.template_string = template_string
    185234        self.origin = origin
    186235        self.lineno = 1
    187         self.verbatim = False
     236        self.verbatim = False   # or 'endverbatim' or 'endverbatim %s' % block
    188237
    189238    def tokenize(self):
    190239        """
    class Lexer(object):  
    192241        """
    193242        in_tag = False
    194243        result = []
    195         for bit in tag_re.split(self.template_string):
     244        tag_splitter = strip_leading_whitespace_tag_re if settings.TEMPLATE_STRIP_LEADING_WHITESPACE else tag_re
     245        for bit in tag_splitter.split(self.template_string):
    196246            if bit:
    197247                result.append(self.create_token(bit, in_tag))
    198248            in_tag = not in_tag
    class Lexer(object):  
    204254        If in_tag is True, we are processing something that matched a tag,
    205255        otherwise it should be treated as a literal string.
    206256        """
    207         if in_tag and token_string.startswith(BLOCK_TAG_START):
     257        # Extract ``block_content``, check for verbatim tag
     258        if in_tag and token_string.lstrip().startswith(BLOCK_TAG_START):
    208259            # The [2:-2] ranges below strip off *_TAG_START and *_TAG_END.
    209260            # We could do len(BLOCK_TAG_START) to be more "correct", but we've
    210261            # hard-coded the 2s here for performance. And it's not like
    class Lexer(object):  
    212263            block_content = token_string[2:-2].strip()
    213264            if self.verbatim and block_content == self.verbatim:
    214265                self.verbatim = False
     266
     267        # Create ``token`` object...
    215268        if in_tag and not self.verbatim:
    216             if token_string.startswith(VARIABLE_TAG_START):
    217                 token = Token(TOKEN_VAR, token_string[2:-2].strip())
    218             elif token_string.startswith(BLOCK_TAG_START):
     269            # ...by processing a...
     270            if token_string.lstrip().startswith(VARIABLE_TAG_START):
     271                # ...variable tag.
     272                token_struct = variable_command_re.search(token_string)
     273                token_string = token_struct.group('token_string')
     274                token = Token(TOKEN_VAR, token_string.strip())
     275            elif token_string.lstrip().startswith(BLOCK_TAG_START):
     276                # ...block tag.
    219277                if block_content[:9] in ('verbatim', 'verbatim '):
    220278                    self.verbatim = 'end%s' % block_content
    221                 token = Token(TOKEN_BLOCK, block_content)
    222             elif token_string.startswith(COMMENT_TAG_START):
     279                token_struct = block_command_re.search(token_string)
     280                token_string = token_struct.group('token_string')
     281                token = Token(TOKEN_BLOCK, token_string.strip())
     282            elif token_string.lstrip().startswith(COMMENT_TAG_START):
     283                # ...comment tag.
    223284                content = ''
     285                token_struct = comment_command_re.search(token_string)
     286                token_string = token_struct.group('token_string')
    224287                if token_string.find(TRANSLATOR_COMMENT_MARK):
    225                     content = token_string[2:-2].strip()
     288                    content = token_string.strip()
    226289                token = Token(TOKEN_COMMENT, content)
    227290        else:
     291            # ...from a string literal.
    228292            token = Token(TOKEN_TEXT, token_string)
     293
    229294        token.lineno = self.lineno
    230295        self.lineno += token_string.count('\n')
    231296        return token
    232297
     298
    233299class Parser(object):
    234300    def __init__(self, tokens):
    235301        self.tokens = tokens
  • django/template/debug.py

    diff --git a/django/template/debug.py b/django/template/debug.py
    index 6167403..3df6681 100644
    a b  
    1 from django.template.base import Lexer, Parser, tag_re, NodeList, VariableNode, TemplateSyntaxError
     1from django.conf import settings
     2from django.template.base import Lexer, Parser, tag_re, strip_leading_whitespace_tag_re, NodeList, VariableNode, TemplateSyntaxError
    23from django.utils.encoding import force_unicode
    34from django.utils.html import escape
    45from django.utils.safestring import SafeData, EscapeData
    class DebugLexer(Lexer):  
    1314    def tokenize(self):
    1415        "Return a list of tokens from a given template_string"
    1516        result, upto = [], 0
    16         for match in tag_re.finditer(self.template_string):
     17        tag_splitter = strip_leading_whitespace_tag_re if settings.TEMPLATE_STRIP_LEADING_WHITESPACE else tag_re
     18        for match in tag_splitter.finditer(self.template_string):
    1719            start, end = match.span()
    1820            if start > upto:
    1921                result.append(self.create_token(self.template_string[upto:start], (upto, start), False))
  • django/template/defaulttags.py

    diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py
    index 83b72e1..2ca094d 100644
    a b from django.template.base import (Node, NodeList, Template, Context, Library,  
    1414    VARIABLE_ATTRIBUTE_SEPARATOR, get_library, token_kwargs, kwarg_re)
    1515from django.template.smartif import IfParser, Literal
    1616from django.template.defaultfilters import date
     17from django.utils import timezone
    1718from django.utils.encoding import smart_unicode
    1819from django.utils.safestring import mark_safe
    19 from django.utils import timezone
     20from django.utils.text import smart_split
     21
    2022
    2123register = Library()
    2224
  • docs/ref/settings.txt

    diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt
    index 627aa50..ce9779e 100644
    a b Default: ``''`` (Empty string)  
    19561956Output, as a string, that the template system should use for invalid (e.g.
    19571957misspelled) variables. See :ref:`invalid-template-variables`..
    19581958
     1959.. setting:: TEMPLATE_STRIP_LEADING_WHITESPACE
     1960
     1961TEMPLATE_STRIP_LEADING_WHITESPACE
     1962---------------------------------
     1963
     1964.. versionadded:: 1.5
     1965
     1966Default: ``False``
     1967
     1968Whether to strip leading whitespace on template lines containing only template
     1969syntax.
     1970
     1971This template code::
     1972    <h1>My list</h1>
     1973
     1974    {# This is a comment #}
     1975    {# It describes the loop below #}
     1976    {# Each line appears in the output as an empty line #}
     1977    <ul>
     1978    {% for item in items %}
     1979        {# Description of a list item #}
     1980        <li>{{ item }}</li>
     1981    {% endfor %}
     1982    </ul>
     1983
     1984Normally evaluates to::
     1985    <h1>My list</h1>
     1986
     1987
     1988
     1989
     1990    <ul>
     1991
     1992
     1993        <li>item 1</li>
     1994
     1995        <li>item 2</li>
     1996
     1997        <li>item 3</li>
     1998
     1999    </ul>
     2000
     2001However, if TEMPLATE_STRIP_LEADING_WHITESPACE is set to True, lines which
     2002contain only one template tag, variable or comment no not cause a newline
     2003to be inserted in the output::
     2004
     2005    <h1>My list</h1>
     2006
     2007    <ul>
     2008        <li>item 1</li>
     2009        <li>item 2</li>
     2010        <li>item 3</li>
     2011    </ul>
     2012
     2013Trailing whitespace does not get stripped, and lines which contain more
     2014than one template tag will not have their leading whitespace trimmed.
     2015
    19592016.. setting:: TEST_RUNNER
    19602017
    19612018TEST_RUNNER
  • tests/regressiontests/templates/tests.py

    diff --git a/tests/regressiontests/templates/tests.py b/tests/regressiontests/templates/tests.py
    index 35d0122..a69fa23 100644
    a b class Templates(unittest.TestCase):  
    387387        except TemplateSyntaxError as e:
    388388            self.assertEqual(e.args[0], "Invalid block tag: 'endblock', expected 'elif', 'else' or 'endif'")
    389389
     390    def test_insignificant_whitespace(self):
     391        whitespace_tests = {
     392            # The test tuple contents is (template_content, context_args, stripped_output, unstripped_output)
     393            # Tags on their own line should collapse the newline before them
     394            # Trailing newline is not removed
     395            # Leading whitespace before single template tag
     396            'insignificant-whitespace01': ('\n {% templatetag openblock %}\n', {}, '{%\n',
     397                                                                                 '\n {%\n'),
     398            'insignificant-whitespace02': ('\n{% templatetag openblock %}\n', {}, '{%\n',
     399                                                                                '\n{%\n'),
     400            'insignificant-whitespace03': ('{% templatetag openblock %}\n', {}, '{%\n',
     401                                                                              '{%\n'),
     402            'insignificant-whitespace04': ('\n\t \t {% templatetag openblock %}\n', {}, '{%\n',
     403                                                                                      '\n\t \t {%\n'),
     404            # Leading whitespace with text before single template tag
     405            'insignificant-whitespace05': ('\n some\ttext {% templatetag openblock %}\n', {}, '\n some\ttext {%\n',
     406                                                                                            '\n some\ttext {%\n'),
     407            # Leading line with text before single template tag
     408            'insignificant-whitespace06': ('\n some\ttext\n {% templatetag openblock %}\n', {}, '\n some\ttext{%\n',
     409                                                                                              '\n some\ttext\n {%\n'),
     410            'insignificant-whitespace07': ('\n some\ttext \n \t {% templatetag openblock %}\n', {}, '\n some\ttext {%\n',
     411                                                                                                  '\n some\ttext \n \t {%\n'),
     412            # whitespace leading /before/ the newline is not stripped.
     413            'insignificant-whitespace08': ('\n some\ttext \t \n {% templatetag openblock %}\n', {}, '\n some\ttext \t {%\n',
     414                                                                                                  '\n some\ttext \t \n {%\n'),
     415            # Multiple text lines before tag
     416            'insignificant-whitespace09': ('\n some\ntext \t \n {% templatetag openblock %}\n', {}, '\n some\ntext \t {%\n',
     417                                                                                                  '\n some\ntext \t \n {%\n'),
     418            'insignificant-whitespace10': ('\n some \t \n \t text \t \n {% templatetag openblock %}\n', {}, '\n some \t \n \t text \t {%\n',
     419                                                                                                          '\n some \t \n \t text \t \n {%\n'),
     420            # Leading whitespace before tag, some text after
     421            'insignificant-whitespace11': ('\n   \t {% templatetag openblock %} some text\n', {}, '\n   \t {% some text\n',
     422                                                                                                '\n   \t {% some text\n'),
     423            # Leading whitespace before tag, some text with trailing whitespace after
     424            'insignificant-whitespace12': ('\n   \t {% templatetag openblock %} some text  \t \n', {}, '\n   \t {% some text  \t \n',
     425                                                                                                     '\n   \t {% some text  \t \n'),
     426            # Whitespace after tag is not removed
     427            'insignificant-whitespace13': ('\n \t {% templatetag openblock %} \t \n \t some text  \t \n', {}, '{% \t \n \t some text  \t \n',
     428                                                                                                            '\n \t {% \t \n \t some text  \t \n'),
     429            # Multiple lines of leading whitespace. Only one leading newline is removed
     430            'insignificant-whitespace14': ('\n\n\n{% templatetag openblock %}\n some text\n', {}, '\n\n{%\n some text\n',
     431                                                                                                '\n\n\n{%\n some text\n'),
     432            # Trailing whitespace after tag
     433            'insignificant-whitespace15': ('\n\n\n{% templatetag openblock %}\t \t \t\n some text\n', {}, '\n\n{%\t \t \t\n some text\n',
     434                                                                                                        '\n\n\n{%\t \t \t\n some text\n'),
     435            # Removable newline followed by leading whitespace
     436            'insignificant-whitespace16': ('\n\n\n\t \t \t{% templatetag openblock %}\n some text\n', {}, '\n\n{%\n some text\n',
     437                                                                                                        '\n\n\n\t \t \t{%\n some text\n'),
     438            # Removable leading whitespace and trailing whitespace
     439            'insignificant-whitespace17': ('\n\n\n\t \t \t{% templatetag openblock %}\t \t \t\n some text\n', {}, '\n\n{%\t \t \t\n some text\n',
     440                                                                                                                '\n\n\n\t \t \t{%\t \t \t\n some text\n'),
     441            # Multiple lines of trailing whitespace. No trailing newline is removed.
     442            'insignificant-whitespace18': ('\n{% templatetag openblock %}\n\n\n some text\n', {}, '{%\n\n\n some text\n',
     443                                                                                                '\n{%\n\n\n some text\n'),
     444            'insignificant-whitespace19': ('\n{% templatetag openblock %}\t \n\n\n some text\n', {}, '{%\t \n\n\n some text\n',
     445                                                                                                   '\n{%\t \n\n\n some text\n'),
     446            # Consecutive trimmed lines with tags strips one newline each
     447            'insignificant-whitespace20': (
     448                '\n{% templatetag openblock %}\n{% templatetag openblock %}\n{% templatetag openblock %}\n some text\n'
     449                , {}, '{%{%{%\n some text\n',
     450                      '\n{%\n{%\n{%\n some text\n'),
     451            # Consecutive trimmed lines with tags strips one newline each. Intermediate newlines are preserved
     452            'insignificant-whitespace21': (
     453                '\n\n{% templatetag openblock %}\n\n{% templatetag openblock %}\n\n{% templatetag openblock %}\n\n some text\n'
     454                , {}, '\n{%\n{%\n{%\n\n some text\n',
     455                      '\n\n{%\n\n{%\n\n{%\n\n some text\n'),
     456            # Consecutive trimmed lines with tags strips one newline each. Leading whitespace is stripped but trailing is not
     457            'insignificant-whitespace22': (
     458                '\n\n\t {% templatetag openblock %}\t \n\n\t {% templatetag openblock %}\t \n\n\t {% templatetag openblock %}\t \n some text\n'
     459                , {}, '\n{%\t \n{%\t \n{%\t \n some text\n',
     460                      '\n\n\t {%\t \n\n\t {%\t \n\n\t {%\t \n some text\n'),
     461            # Consecutive trimmed lines with tags strips one newline each. Intermediate whitespace is stripped
     462            'insignificant-whitespace23': (
     463                '\n\t {% templatetag openblock %}\t \n\t {% templatetag openblock %}\t \n\t {% templatetag openblock %}\t \n some text\n'
     464                , {}, '{%\t {%\t {%\t \n some text\n',
     465                      '\n\t {%\t \n\t {%\t \n\t {%\t \n some text\n'),
     466            # Intermediate whitespace on one line is preserved
     467            # Consecutive tags on one line do not have intermediate whitespace or leading whitespace stripped
     468            'insignificant-whitespace24': (
     469                '\n\t {% templatetag openblock %}\t \t {% templatetag openblock %}\t \t {% templatetag openblock %}\t \n some text\n'
     470                , {}, '\n\t {%\t \t {%\t \t {%\t \n some text\n',
     471                      '\n\t {%\t \t {%\t \t {%\t \n some text\n'),
     472            # Still, only one leading newline is removed.
     473            'insignificant-whitespace25': (
     474                '\n\n {% templatetag openblock %}\n \t {% templatetag openblock %}\n \t {% templatetag openblock %}\n some text\n'
     475                , {}, '\n{%{%{%\n some text\n',
     476                      '\n\n {%\n \t {%\n \t {%\n some text\n'),
     477            # Lines with {# comments #} have the same stripping behavior
     478            'insignificant-whitespace26': (
     479                '\n\n {% templatetag openblock %}\n \t {# some comment #}\n some text\n'
     480                , {}, '\n{%\n some text\n',
     481                      '\n\n {%\n \t \n some text\n'),
     482            # Only {# comments #}
     483            'insignificant-whitespace27': (
     484                '\n\n {# a comment #}\n \t {# some comment #}\n some text\n'
     485                , {}, '\n\n some text\n',
     486                      '\n\n \n \t \n some text\n'),
     487            # Consecutive newlines with tags and comments
     488            'insignificant-whitespace28': (
     489                '\n\t {% templatetag openblock %}\t \n\t {# some comment #}\t \n\t {% templatetag openblock %}\t \n some text\n'
     490                , {}, '{%\t \t {%\t \n some text\n',
     491                      '\n\t {%\t \n\t \t \n\t {%\t \n some text\n'),
     492
     493            # Lines with only {{ values }} have the same stripping behavior
     494            'insignificant-whitespace29': (
     495                '\n {% templatetag openblock %}\t\n \t {{ spam }}\t \n \t {% templatetag openblock %}\t \n some text\n'
     496                , {"spam" : "ham"}, '{%\tham\t {%\t \n some text\n',
     497                                    '\n {%\t\n \t ham\t \n \t {%\t \n some text\n'),
     498            'insignificant-whitespace30': (
     499                '\n\n {% templatetag openblock %}\t\n\n \t {{ spam }}\t \n\n \t {% templatetag openblock %}\t \n some text\n'
     500                , {"spam" : "ham"}, '\n{%\t\nham\t \n{%\t \n some text\n',
     501                                    '\n\n {%\t\n\n \t ham\t \n\n \t {%\t \n some text\n'),
     502            ## Leading whitespace not stripped when followed by anything. See insignificant-whitespace24
     503            'insignificant-whitespace31': (
     504                '\n {% templatetag openblock %}\t \t {{ spam }}\t \t {% templatetag openblock %}\t \n some text\n'
     505                , {"spam" : "ham"}, '\n {%\t \t ham\t \t {%\t \n some text\n',
     506                                    '\n {%\t \t ham\t \t {%\t \n some text\n'),
     507            #  {{ value }} {% tag %} {{ value }} this time
     508            'insignificant-whitespace32': (
     509                '\n {{ spam }}\t\n \t {% templatetag openblock %}\t \n \t {{ spam }}\t \n some text\n'
     510                , {"spam" : "ham"}, 'ham\t{%\t ham\t \n some text\n',
     511                                    '\n ham\t\n \t {%\t \n \t ham\t \n some text\n'),
     512
     513            # Invalid stuff is still invalid
     514            # Newlines inside begin-end tokens, even in {# comments #}, make it not a tag.
     515            'insignificant-whitespace33': (
     516                '\n\n {# \n{% templatetag openblock #}\t \n some text\n'
     517                , {}, '\n\n {# \n{% templatetag openblock #}\t \n some text\n',
     518                      '\n\n {# \n{% templatetag openblock #}\t \n some text\n'),
     519            # Complete comment matching tags on one line are processed
     520            'insignificant-whitespace34': (
     521                '\n\n {# \n{# templatetag openblock #}\t \n some text\n'
     522                , {}, '\n\n {# \t \n some text\n',
     523                      '\n\n {# \n\t \n some text\n'),
     524            'insignificant-whitespace35': (
     525                '\n\n {# \n{# templatetag openblock\n #}\t \n some text\n'
     526                , {}, '\n\n {# \n{# templatetag openblock\n #}\t \n some text\n',
     527                      '\n\n {# \n{# templatetag openblock\n #}\t \n some text\n'),
     528            'insignificant-whitespace36': (
     529                '\n\n {# \n{{ some comment #}\t \n some text\n'
     530                , {}, '\n\n {# \n{{ some comment #}\t \n some text\n',
     531                      '\n\n {# \n{{ some comment #}\t \n some text\n'),
     532            'insignificant-whitespace37': (
     533                '\n\n {# \n \t {% templatetag openblock #}\t \n some text\n'
     534                , {}, '\n\n {# \n \t {% templatetag openblock #}\t \n some text\n',
     535                      '\n\n {# \n \t {% templatetag openblock #}\t \n some text\n'),
     536            'insignificant-whitespace38': (
     537                "\n\n {# templatetag openblock #\n}\t \n some text\n"
     538                , {}, "\n\n {# templatetag openblock #\n}\t \n some text\n",
     539                      "\n\n {# templatetag openblock #\n}\t \n some text\n" ),
     540            'insignificant-whitespace39': (
     541                "\n\n {% templatetag openblock %\n}\t \n some text\n"
     542                , {}, "\n\n {% templatetag openblock %\n}\t \n some text\n",
     543                      "\n\n {% templatetag openblock %\n}\t \n some text\n" ),
     544            'insignificant-whitespace40': (
     545                "\n\n {{ templatetag openblock }\n}\t \n some text\n"
     546                , {}, "\n\n {{ templatetag openblock }\n}\t \n some text\n",
     547                      "\n\n {{ templatetag openblock }\n}\t \n some text\n" ),
     548            'insignificant-whitespace41': (
     549                "\n\n {\n# {# templatetag openblock #}\t \n some text\n"
     550                , {}, "\n\n {\n# \t \n some text\n",
     551                      "\n\n {\n# \t \n some text\n"),
     552            'insignificant-whitespace42': (
     553                "\n\n {\n {# templatetag openblock #}\t \n some text\n"
     554                , {}, "\n\n {\t \n some text\n",
     555                      "\n\n {\n \t \n some text\n"),
     556            'insignificant-whitespace43': (
     557                "\n{{# foo #};{# bar #}\n"
     558                , {}, "\n{;\n",
     559                      "\n{;\n"),
     560          }
     561        tests = whitespace_tests.items()
     562        tests.sort()
     563
     564        # Register our custom template loader.
     565        def test_whitespace_loader(template_name, template_dirs=None):
     566            "A custom template loader that loads the unit-test templates."
     567            try:
     568                return (whitespace_tests[template_name][0] , "test:%s" % template_name)
     569            except KeyError:
     570                raise template.TemplateDoesNotExist, template_name
     571
     572        old_template_loaders = loader.template_source_loaders
     573        loader.template_source_loaders = [test_whitespace_loader]
     574
     575        failures = []
     576
     577        old_strip_leading_whitespace = settings.TEMPLATE_STRIP_LEADING_WHITESPACE
     578
     579        for name, vals in tests:
     580            for strip_leading_whitespace in (True, False):
     581                settings.TEMPLATE_STRIP_LEADING_WHITESPACE = strip_leading_whitespace
     582                test_template = loader.get_template(name)
     583                result = vals[2] if strip_leading_whitespace else vals[3]
     584                output = self.render(test_template, vals)
     585
     586                if output != result:
     587                    failures.append("Whitespace test: %s -- FAILED. Expected %r, got %r" % (name, result, output))
     588
     589        loader.template_source_loaders = old_template_loaders
     590        settings.TEMPLATE_STRIP_LEADING_WHITESPACE = old_strip_leading_whitespace
     591
     592        self.assertEqual(failures, [], "Tests failed:\n%s\n%s" %
     593            ('-'*70, ("\n%s\n" % ('-'*70)).join(failures)))
     594
    390595    def test_templates(self):
    391596        template_tests = self.get_template_tests()
    392597        filter_tests = filters.get_filter_tests()
Back to Top