Code

Ticket #2594: whitespace.patch

File whitespace.patch, 24.8 KB (added by steveire, 3 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()