Ticket #2594: whitespace.patch

File whitespace.patch, 24.8 KB (added by Stephen Kelly, 13 years ago)
  • AUTHORS

    commit ffaecbd5cf0387868af908f89e0b6695de755174
    Author: Stephen Kelly <steveire@gmail.com>
    Date:   Thu Jan 20 18:52:36 2011 +0100
    
        Handle whitespace better in the template system.
    
    diff --git a/AUTHORS b/AUTHORS
    index ffcc34d..32dba07 100644
    a b answer newbie questions, and generally made Django that much better:  
    262262    Ian G. Kelly <ian.g.kelly@gmail.com>
    263263    Niall Kelly <duke.sam.vimes@gmail.com>
    264264    Ryan Kelly <ryan@rfk.id.au>
     265    Stephen Kelly <steveire@gmail.com>
    265266    Thomas Kerpe <thomas@kerpe.net>
    266267    Wiley Kestner <wiley.kestner@gmail.com>
    267268    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 0017f46..e5c0dbf 100644
    a b gettext_noop = lambda s: s  
    1111
    1212DEBUG = False
    1313TEMPLATE_DEBUG = False
     14TEMPLATE_STRIP_LEADING_WHITESPACE = False
    1415
    1516# Whether the framework should propagate raw exceptions rather than catching
    1617# them. This is useful under some testing siutations and should never be used
  • django/template/base.py

    diff --git a/django/template/base.py b/django/template/base.py
    index 1440869..9211ebf 100644
    a b  
    1 import imp
    21import re
    32from inspect import getargspec
    43
    from django.conf import settings  
    65from django.template.context import Context, RequestContext, ContextPopException
    76from django.utils.importlib import import_module
    87from django.utils.itercompat import is_iterable
    9 from django.utils.functional import curry, Promise
     8from django.utils.functional import curry
    109from django.utils.text import smart_split, unescape_string_literal, get_text_list
    1110from django.utils.encoding import smart_unicode, force_unicode, smart_str
    1211from django.utils.translation import ugettext as _
    ALLOWED_VARIABLE_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01  
    4140# (e.g. strings)
    4241UNKNOWN_SOURCE = '<unknown source>'
    4342
     43
    4444# match a variable or block tag and capture the entire tag, including start/end delimiters
    45 tag_re = re.compile('(%s.*?%s|%s.*?%s|%s.*?%s)' % (re.escape(BLOCK_TAG_START), re.escape(BLOCK_TAG_END),
    46                                           re.escape(VARIABLE_TAG_START), re.escape(VARIABLE_TAG_END),
    47                                           re.escape(COMMENT_TAG_START), re.escape(COMMENT_TAG_END)))
     45tag_re = re.compile('''
     46    (
     47          %(BLOCK_TAG_START)s.*?%(BLOCK_TAG_END)s        # block start + inner + end
     48        | %(VARIABLE_TAG_START)s.*?%(VARIABLE_TAG_END)s  # variable start + inner + end
     49        | %(COMMENT_TAG_START)s.*?%(COMMENT_TAG_END)s    # comment start + inner + end
     50    )
     51    ''' % {
     52        'BLOCK_TAG_START': re.escape(BLOCK_TAG_START),
     53        'BLOCK_TAG_END': re.escape(BLOCK_TAG_END),
     54        'VARIABLE_TAG_START': re.escape(VARIABLE_TAG_START),
     55        'VARIABLE_TAG_END': re.escape(VARIABLE_TAG_END),
     56        'COMMENT_TAG_START': re.escape(COMMENT_TAG_START),
     57        'COMMENT_TAG_END': re.escape(COMMENT_TAG_END),
     58    }, re.VERBOSE)
     59
     60strip_leading_whitespace_tag_re = re.compile('''
     61    (
     62    [\n\r]{1}     # 1 vertical white space (the one starting this line)
     63    [ \t]*?       # any number of tabs or spaces
     64    (?:           # group but don't match
     65        %(BLOCK_TAG_START)s
     66        [^\n\r%(BLOCK_TAG_START)s%(BLOCK_TAG_END)s]*?       # anything *not* vertical whitespace or
     67        %(BLOCK_TAG_END)s                                   # opening/closing block tag
     68    |
     69        %(COMMENT_TAG_START)s
     70        [^\n\r%(COMMENT_TAG_START)s%(COMMENT_TAG_END)s]*?   # anything *not* vertical whitespace or
     71        %(COMMENT_TAG_END)s                                 # opening/closing comment tag
     72    |
     73        %(VARIABLE_TAG_START)s
     74        [^\n\r%(VARIABLE_TAG_START)s%(VARIABLE_TAG_END)s]*? # anything *not* vertical whitespace or
     75        %(VARIABLE_TAG_END)s                                # opening/closing variable tag
     76    )
     77    (?=                      # Match this only if the previous matched
     78        [ \t]*?
     79        [\n\r]+
     80    )|
     81          %(BLOCK_TAG_START)s.*?%(BLOCK_TAG_END)s           # block start + inner + end
     82        | %(VARIABLE_TAG_START)s.*?%(VARIABLE_TAG_END)s     # variable start + inner + end
     83        | %(COMMENT_TAG_START)s.*?%(COMMENT_TAG_END)s       # comment start + inner + end
     84    )''' % {
     85        'BLOCK_TAG_START': re.escape(BLOCK_TAG_START),
     86        'BLOCK_TAG_END': re.escape(BLOCK_TAG_END),
     87        'VARIABLE_TAG_START': re.escape(VARIABLE_TAG_START),
     88        'VARIABLE_TAG_END': re.escape(VARIABLE_TAG_END),
     89        'COMMENT_TAG_START': re.escape(COMMENT_TAG_START),
     90        'COMMENT_TAG_END': re.escape(COMMENT_TAG_END),
     91    }, re.VERBOSE)
     92
     93# find the command within a block tag
     94block_command_re = re.compile('%s(?P<token_string>.*?)%s' % (re.escape(BLOCK_TAG_START), re.escape(BLOCK_TAG_END)))
     95comment_command_re = re.compile('%s(?P<token_string>.*?)%s' % (re.escape(COMMENT_TAG_START), re.escape(COMMENT_TAG_END)))
     96variable_command_re = re.compile('%s(?P<token_string>.*?)%s' % (re.escape(VARIABLE_TAG_START), re.escape(VARIABLE_TAG_END)))
    4897
    4998# global dictionary of libraries that have been loaded using get_library
    5099libraries = {}
    class Lexer(object):  
    171220        "Return a list of tokens from a given template_string."
    172221        in_tag = False
    173222        result = []
    174         for bit in tag_re.split(self.template_string):
     223        tag_splitter = strip_leading_whitespace_tag_re if settings.TEMPLATE_STRIP_LEADING_WHITESPACE else tag_re
     224        for bit in tag_splitter.split(self.template_string):
    175225            if bit:
    176226                result.append(self.create_token(bit, in_tag))
    177227            in_tag = not in_tag
    class Lexer(object):  
    184234        otherwise it should be treated as a literal string.
    185235        """
    186236        if in_tag:
    187             if token_string.startswith(VARIABLE_TAG_START):
    188                 token = Token(TOKEN_VAR, token_string[len(VARIABLE_TAG_START):-len(VARIABLE_TAG_END)].strip())
    189             elif token_string.startswith(BLOCK_TAG_START):
    190                 token = Token(TOKEN_BLOCK, token_string[len(BLOCK_TAG_START):-len(BLOCK_TAG_END)].strip())
    191             elif token_string.startswith(COMMENT_TAG_START):
     237            if token_string.lstrip().startswith(VARIABLE_TAG_START):
     238                token_struct = variable_command_re.search(token_string)
     239                token_string = token_struct.group('token_string')
     240                token = Token(TOKEN_VAR, token_string.strip())
     241            elif token_string.lstrip().startswith(BLOCK_TAG_START):
     242                token_struct = block_command_re.search(token_string)
     243                token_string = token_struct.group('token_string')
     244                token = Token(TOKEN_BLOCK, token_string.strip())
     245            elif token_string.lstrip().startswith(COMMENT_TAG_START):
    192246                content = ''
     247                token_struct = comment_command_re.search(token_string)
     248                token_string = token_struct.group('token_string')
    193249                if token_string.find(TRANSLATOR_COMMENT_MARK):
    194250                    content = token_string[len(COMMENT_TAG_START):-len(COMMENT_TAG_END)].strip()
    195251                token = Token(TOKEN_COMMENT, content)
  • django/template/defaulttags.py

    diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py
    index 0662b5f..13d4759 100644
    a b from django.template.smartif import IfParser, Literal  
    1111from django.conf import settings
    1212from django.utils.encoding import smart_str, smart_unicode
    1313from django.utils.safestring import mark_safe
     14from django.utils.text import smart_split
    1415
    1516register = Library()
    1617# Regex for token keyword arguments
    def token_kwargs(bits, parser, support_legacy=False):  
    4748
    4849    kwargs = {}
    4950    while bits:
    50         if kwarg_format: 
     51        if kwarg_format:
    5152            match = kwarg_re.match(bits[0])
    5253            if not match or not match.group(1):
    5354                return kwargs
  • docs/ref/settings.txt

    diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt
    index 87a71fe..4addda6 100644
    a b misspelled) variables. See :ref:`invalid-template-variables`..  
    17551755
    17561756.. setting:: TEST_RUNNER
    17571757
     1758TEMPLATE_STRIP_LEADING_WHITESPACE
     1759---------------------------------
     1760
     1761.. versionadded:: 1.3
     1762
     1763Default: ``False``
     1764
     1765Whether to strip leading whitespace on template lines containing only template
     1766syntax.
     1767
     1768This template code::
     1769    <h1>My list</h1>
     1770
     1771    {# This is a comment #}
     1772    {# It describes the loop below #}
     1773    {# Each line appears in the output as an empty line #}
     1774    <ul>
     1775    {% for item in items %}
     1776        {# Description of a list item #}
     1777        <li>{{ item }}</li>
     1778    {% endfor %}
     1779    </ul>
     1780
     1781Normally evaluates to::
     1782    <h1>My list</h1>
     1783
     1784
     1785
     1786
     1787    <ul>
     1788
     1789
     1790        <li>item 1</li>
     1791
     1792        <li>item 2</li>
     1793
     1794        <li>item 3</li>
     1795
     1796    </ul>
     1797
     1798However, if TEMPLATE_STRIP_LEADING_WHITESPACE is set to True, lines which
     1799contain only one template tag, variable or comment no not cause a newline
     1800to be inserted in the output::
     1801
     1802    <h1>My list</h1>
     1803
     1804    <ul>
     1805        <li>item 1</li>
     1806        <li>item 2</li>
     1807        <li>item 3</li>
     1808    </ul>
     1809
     1810Trailing whitespace does not get stripped, and lines which contain more
     1811than one template tag will not have their leading whitespace trimmed.
     1812
    17581813TEST_RUNNER
    17591814-----------
    17601815
  • tests/regressiontests/templates/tests.py

    diff --git a/tests/regressiontests/templates/tests.py b/tests/regressiontests/templates/tests.py
    index 99776e6..fb176fe 100644
    a b class Templates(unittest.TestCase):  
    340340        except TemplateSyntaxError, e:
    341341            self.assertEquals(e.args[0], "Invalid block tag: 'endblock', expected 'else' or 'endif'")
    342342
     343    def test_insignificant_whitespace(self):
     344        whitespace_tests = {
     345            # The test tuple contents is (template_content, context_args, stripped_output, unstripped_output)
     346            # Tags on their own line should collapse the newline before them
     347            # Trailing newline is not removed
     348            # Leading whitespace before single template tag
     349            'insignificant-whitespace01': ('\n {% templatetag openblock %}\n', {}, '{%\n',
     350                                                                                 '\n {%\n'),
     351            'insignificant-whitespace02': ('\n{% templatetag openblock %}\n', {}, '{%\n',
     352                                                                                '\n{%\n'),
     353            'insignificant-whitespace03': ('{% templatetag openblock %}\n', {}, '{%\n',
     354                                                                              '{%\n'),
     355            'insignificant-whitespace04': ('\n\t \t {% templatetag openblock %}\n', {}, '{%\n',
     356                                                                                      '\n\t \t {%\n'),
     357            # Leading whitespace with text before single template tag
     358            'insignificant-whitespace05': ('\n some\ttext {% templatetag openblock %}\n', {}, '\n some\ttext {%\n',
     359                                                                                            '\n some\ttext {%\n'),
     360            # Leading line with text before single template tag
     361            'insignificant-whitespace06': ('\n some\ttext\n {% templatetag openblock %}\n', {}, '\n some\ttext{%\n',
     362                                                                                              '\n some\ttext\n {%\n'),
     363            'insignificant-whitespace07': ('\n some\ttext \n \t {% templatetag openblock %}\n', {}, '\n some\ttext {%\n',
     364                                                                                                  '\n some\ttext \n \t {%\n'),
     365            # whitespace leading /before/ the newline is not stripped.
     366            'insignificant-whitespace08': ('\n some\ttext \t \n {% templatetag openblock %}\n', {}, '\n some\ttext \t {%\n',
     367                                                                                                  '\n some\ttext \t \n {%\n'),
     368            # Multiple text lines before tag
     369            'insignificant-whitespace09': ('\n some\ntext \t \n {% templatetag openblock %}\n', {}, '\n some\ntext \t {%\n',
     370                                                                                                  '\n some\ntext \t \n {%\n'),
     371            'insignificant-whitespace10': ('\n some \t \n \t text \t \n {% templatetag openblock %}\n', {}, '\n some \t \n \t text \t {%\n',
     372                                                                                                          '\n some \t \n \t text \t \n {%\n'),
     373            # Leading whitespace before tag, some text after
     374            'insignificant-whitespace11': ('\n   \t {% templatetag openblock %} some text\n', {}, '\n   \t {% some text\n',
     375                                                                                                '\n   \t {% some text\n'),
     376            # Leading whitespace before tag, some text with trailing whitespace after
     377            'insignificant-whitespace12': ('\n   \t {% templatetag openblock %} some text  \t \n', {}, '\n   \t {% some text  \t \n',
     378                                                                                                     '\n   \t {% some text  \t \n'),
     379            # Whitespace after tag is not removed
     380            'insignificant-whitespace13': ('\n \t {% templatetag openblock %} \t \n \t some text  \t \n', {}, '{% \t \n \t some text  \t \n',
     381                                                                                                            '\n \t {% \t \n \t some text  \t \n'),
     382            # Multiple lines of leading whitespace. Only one leading newline is removed
     383            'insignificant-whitespace14': ('\n\n\n{% templatetag openblock %}\n some text\n', {}, '\n\n{%\n some text\n',
     384                                                                                                '\n\n\n{%\n some text\n'),
     385            # Trailing whitespace after tag
     386            'insignificant-whitespace15': ('\n\n\n{% templatetag openblock %}\t \t \t\n some text\n', {}, '\n\n{%\t \t \t\n some text\n',
     387                                                                                                        '\n\n\n{%\t \t \t\n some text\n'),
     388            # Removable newline followed by leading whitespace
     389            'insignificant-whitespace16': ('\n\n\n\t \t \t{% templatetag openblock %}\n some text\n', {}, '\n\n{%\n some text\n',
     390                                                                                                        '\n\n\n\t \t \t{%\n some text\n'),
     391            # Removable leading whitespace and trailing whitespace
     392            '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',
     393                                                                                                                '\n\n\n\t \t \t{%\t \t \t\n some text\n'),
     394            # Multiple lines of trailing whitespace. No trailing newline is removed.
     395            'insignificant-whitespace18': ('\n{% templatetag openblock %}\n\n\n some text\n', {}, '{%\n\n\n some text\n',
     396                                                                                                '\n{%\n\n\n some text\n'),
     397            'insignificant-whitespace19': ('\n{% templatetag openblock %}\t \n\n\n some text\n', {}, '{%\t \n\n\n some text\n',
     398                                                                                                   '\n{%\t \n\n\n some text\n'),
     399            # Consecutive trimmed lines with tags strips one newline each
     400            'insignificant-whitespace20': (
     401                '\n{% templatetag openblock %}\n{% templatetag openblock %}\n{% templatetag openblock %}\n some text\n'
     402                , {}, '{%{%{%\n some text\n',
     403                      '\n{%\n{%\n{%\n some text\n'),
     404            # Consecutive trimmed lines with tags strips one newline each. Intermediate newlines are preserved
     405            'insignificant-whitespace21': (
     406                '\n\n{% templatetag openblock %}\n\n{% templatetag openblock %}\n\n{% templatetag openblock %}\n\n some text\n'
     407                , {}, '\n{%\n{%\n{%\n\n some text\n',
     408                      '\n\n{%\n\n{%\n\n{%\n\n some text\n'),
     409            # Consecutive trimmed lines with tags strips one newline each. Leading whitespace is stripped but trailing is not
     410            'insignificant-whitespace22': (
     411                '\n\n\t {% templatetag openblock %}\t \n\n\t {% templatetag openblock %}\t \n\n\t {% templatetag openblock %}\t \n some text\n'
     412                , {}, '\n{%\t \n{%\t \n{%\t \n some text\n',
     413                      '\n\n\t {%\t \n\n\t {%\t \n\n\t {%\t \n some text\n'),
     414            # Consecutive trimmed lines with tags strips one newline each. Intermediate whitespace is stripped
     415            'insignificant-whitespace23': (
     416                '\n\t {% templatetag openblock %}\t \n\t {% templatetag openblock %}\t \n\t {% templatetag openblock %}\t \n some text\n'
     417                , {}, '{%\t {%\t {%\t \n some text\n',
     418                      '\n\t {%\t \n\t {%\t \n\t {%\t \n some text\n'),
     419            # Intermediate whitespace on one line is preserved
     420            # Consecutive tags on one line do not have intermediate whitespace or leading whitespace stripped
     421            'insignificant-whitespace24': (
     422                '\n\t {% templatetag openblock %}\t \t {% templatetag openblock %}\t \t {% templatetag openblock %}\t \n some text\n'
     423                , {}, '\n\t {%\t \t {%\t \t {%\t \n some text\n',
     424                      '\n\t {%\t \t {%\t \t {%\t \n some text\n'),
     425            # Still, only one leading newline is removed.
     426            'insignificant-whitespace25': (
     427                '\n\n {% templatetag openblock %}\n \t {% templatetag openblock %}\n \t {% templatetag openblock %}\n some text\n'
     428                , {}, '\n{%{%{%\n some text\n',
     429                      '\n\n {%\n \t {%\n \t {%\n some text\n'),
     430            # Lines with {# comments #} have the same stripping behavior
     431            'insignificant-whitespace26': (
     432                '\n\n {% templatetag openblock %}\n \t {# some comment #}\n some text\n'
     433                , {}, '\n{%\n some text\n',
     434                      '\n\n {%\n \t \n some text\n'),
     435            # Only {# comments #}
     436            'insignificant-whitespace27': (
     437                '\n\n {# a comment #}\n \t {# some comment #}\n some text\n'
     438                , {}, '\n\n some text\n',
     439                      '\n\n \n \t \n some text\n'),
     440            # Consecutive newlines with tags and comments
     441            'insignificant-whitespace28': (
     442                '\n\t {% templatetag openblock %}\t \n\t {# some comment #}\t \n\t {% templatetag openblock %}\t \n some text\n'
     443                , {}, '{%\t \t {%\t \n some text\n',
     444                      '\n\t {%\t \n\t \t \n\t {%\t \n some text\n'),
     445
     446            # Lines with only {{ values }} have the same stripping behavior
     447            'insignificant-whitespace29': (
     448                '\n {% templatetag openblock %}\t\n \t {{ spam }}\t \n \t {% templatetag openblock %}\t \n some text\n'
     449                , {"spam" : "ham"}, '{%\tham\t {%\t \n some text\n',
     450                                    '\n {%\t\n \t ham\t \n \t {%\t \n some text\n'),
     451            'insignificant-whitespace30': (
     452                '\n\n {% templatetag openblock %}\t\n\n \t {{ spam }}\t \n\n \t {% templatetag openblock %}\t \n some text\n'
     453                , {"spam" : "ham"}, '\n{%\t\nham\t \n{%\t \n some text\n',
     454                                    '\n\n {%\t\n\n \t ham\t \n\n \t {%\t \n some text\n'),
     455            ## Leading whitespace not stripped when followed by anything. See insignificant-whitespace24
     456            'insignificant-whitespace31': (
     457                '\n {% templatetag openblock %}\t \t {{ spam }}\t \t {% templatetag openblock %}\t \n some text\n'
     458                , {"spam" : "ham"}, '\n {%\t \t ham\t \t {%\t \n some text\n',
     459                                    '\n {%\t \t ham\t \t {%\t \n some text\n'),
     460            #  {{ value }} {% tag %} {{ value }} this time
     461            'insignificant-whitespace32': (
     462                '\n {{ spam }}\t\n \t {% templatetag openblock %}\t \n \t {{ spam }}\t \n some text\n'
     463                , {"spam" : "ham"}, 'ham\t{%\t ham\t \n some text\n',
     464                                    '\n ham\t\n \t {%\t \n \t ham\t \n some text\n'),
     465
     466            # Invalid stuff is still invalid
     467            # Newlines inside begin-end tokens, even in {# comments #}, make it not a tag.
     468            'insignificant-whitespace33': (
     469                '\n\n {# \n{% templatetag openblock #}\t \n some text\n'
     470                , {}, '\n\n {# \n{% templatetag openblock #}\t \n some text\n',
     471                      '\n\n {# \n{% templatetag openblock #}\t \n some text\n'),
     472            # Complete comment matching tags on one line are processed
     473            'insignificant-whitespace34': (
     474                '\n\n {# \n{# templatetag openblock #}\t \n some text\n'
     475                , {}, '\n\n {# \t \n some text\n',
     476                      '\n\n {# \n\t \n some text\n'),
     477            'insignificant-whitespace35': (
     478                '\n\n {# \n{# templatetag openblock\n #}\t \n some text\n'
     479                , {}, '\n\n {# \n{# templatetag openblock\n #}\t \n some text\n',
     480                      '\n\n {# \n{# templatetag openblock\n #}\t \n some text\n'),
     481            'insignificant-whitespace36': (
     482                '\n\n {# \n{{ some comment #}\t \n some text\n'
     483                , {}, '\n\n {# \n{{ some comment #}\t \n some text\n',
     484                      '\n\n {# \n{{ some comment #}\t \n some text\n'),
     485            'insignificant-whitespace37': (
     486                '\n\n {# \n \t {% templatetag openblock #}\t \n some text\n'
     487                , {}, '\n\n {# \n \t {% templatetag openblock #}\t \n some text\n',
     488                      '\n\n {# \n \t {% templatetag openblock #}\t \n some text\n'),
     489            'insignificant-whitespace38': (
     490                "\n\n {# templatetag openblock #\n}\t \n some text\n"
     491                , {}, "\n\n {# templatetag openblock #\n}\t \n some text\n",
     492                      "\n\n {# templatetag openblock #\n}\t \n some text\n" ),
     493            'insignificant-whitespace39': (
     494                "\n\n {% templatetag openblock %\n}\t \n some text\n"
     495                , {}, "\n\n {% templatetag openblock %\n}\t \n some text\n",
     496                      "\n\n {% templatetag openblock %\n}\t \n some text\n" ),
     497            'insignificant-whitespace40': (
     498                "\n\n {{ templatetag openblock }\n}\t \n some text\n"
     499                , {}, "\n\n {{ templatetag openblock }\n}\t \n some text\n",
     500                      "\n\n {{ templatetag openblock }\n}\t \n some text\n" ),
     501            'insignificant-whitespace41': (
     502                "\n\n {\n# {# templatetag openblock #}\t \n some text\n"
     503                , {}, "\n\n {\n# \t \n some text\n",
     504                      "\n\n {\n# \t \n some text\n"),
     505            'insignificant-whitespace42': (
     506                "\n\n {\n {# templatetag openblock #}\t \n some text\n"
     507                , {}, "\n\n {\t \n some text\n",
     508                      "\n\n {\n \t \n some text\n"),
     509            'insignificant-whitespace43': (
     510                "\n{{# foo #};{# bar #}\n"
     511                , {}, "\n{;\n",
     512                      "\n{;\n"),
     513          }
     514        tests = whitespace_tests.items()
     515        tests.sort()
     516
     517        # Register our custom template loader.
     518        def test_whitespace_loader(template_name, template_dirs=None):
     519            "A custom template loader that loads the unit-test templates."
     520            try:
     521                return (whitespace_tests[template_name][0] , "test:%s" % template_name)
     522            except KeyError:
     523                raise template.TemplateDoesNotExist, template_name
     524
     525        old_template_loaders = loader.template_source_loaders
     526        loader.template_source_loaders = [test_whitespace_loader]
     527
     528        failures = []
     529
     530        old_strip_leading_whitespace = settings.TEMPLATE_STRIP_LEADING_WHITESPACE
     531
     532        for name, vals in tests:
     533            for strip_leading_whitespace in (True, False):
     534                settings.TEMPLATE_STRIP_LEADING_WHITESPACE = strip_leading_whitespace
     535                test_template = loader.get_template(name)
     536                result = vals[2] if strip_leading_whitespace else vals[3]
     537                output = self.render(test_template, vals)
     538
     539                if output != result:
     540                    failures.append("Whitespace test: %s -- FAILED. Expected %r, got %r" % (name, result, output))
     541
     542        loader.template_source_loaders = old_template_loaders
     543        settings.TEMPLATE_STRIP_LEADING_WHITESPACE = old_strip_leading_whitespace
     544
     545        self.assertEqual(failures, [], "Tests failed:\n%s\n%s" %
     546            ('-'*70, ("\n%s\n" % ('-'*70)).join(failures)))
     547
    343548    def test_templates(self):
    344549        template_tests = self.get_template_tests()
    345550        filter_tests = filters.get_filter_tests()
Back to Top