diff options
author | W. Trevor King <wking@tremily.us> | 2013-01-11 08:23:24 -0500 |
---|---|---|
committer | W. Trevor King <wking@tremily.us> | 2013-01-11 08:47:30 -0500 |
commit | 7e912c6dfa7a17dd1334ecb1a8d57dd5722b633a (patch) | |
tree | 5072fa0f8dda217a8d26c5a56b11521b167f12c5 | |
parent | 21a2010bf2768bc658e09666c2135063ce004efc (diff) | |
download | jinja2-7e912c6dfa7a17dd1334ecb1a8d57dd5722b633a.tar.gz |
Add `keep_trailing_newline` to configure final endline stripping
This option defaults to False for compatibility with the existing
behaviour. I've added the option because I expect I won't remember to
keep an extra trailing newline in my templates, and some non-HTML
templates *need* that last newline.
See also:
https://groups.google.com/d/msg/pocoo-libs/6DylMqq1voI/GXTbZJ1Tr-sJ
-rw-r--r-- | CHANGES | 2 | ||||
-rw-r--r-- | docs/templates.rst | 4 | ||||
-rw-r--r-- | jinja2/defaults.py | 1 | ||||
-rw-r--r-- | jinja2/environment.py | 14 | ||||
-rw-r--r-- | jinja2/ext.py | 4 | ||||
-rw-r--r-- | jinja2/lexer.py | 13 | ||||
-rw-r--r-- | jinja2/testsuite/lexnparse.py | 13 |
7 files changed, 45 insertions, 6 deletions
@@ -13,6 +13,8 @@ Version 2.7 - Added `urlencode` filter that automatically quotes values for URL safe usage with utf-8 as only supported encoding. If applications want to change this encoding they can override the filter. +- Added `keep-trailing-newline` configuration to environments and + templates to optionally preserve the final trailing newline. - Accessing `last` on the loop context no longer causes the iterator to be consumed into a list. diff --git a/docs/templates.rst b/docs/templates.rst index 790b95e..3526a55 100644 --- a/docs/templates.rst +++ b/docs/templates.rst @@ -156,7 +156,9 @@ In the default configuration, a single trailing newline is stripped if present, and whitespace is not further modified by the template engine. Each whitespace (spaces, tabs, newlines etc.) is returned unchanged. If the application configures Jinja to `trim_blocks` the first newline after a a -template tag is removed automatically (like in PHP). +template tag is removed automatically (like in PHP). To keep the +single trailing newline when it is present, configure Jinja to +`keep_trailing_newline`. But you can also strip whitespace in templates by hand. If you put an minus sign (``-``) to the start or end of an block (for example a for tag), a diff --git a/jinja2/defaults.py b/jinja2/defaults.py index d2d4544..887262f 100644 --- a/jinja2/defaults.py +++ b/jinja2/defaults.py @@ -22,6 +22,7 @@ LINE_STATEMENT_PREFIX = None LINE_COMMENT_PREFIX = None TRIM_BLOCKS = False NEWLINE_SEQUENCE = '\n' +KEEP_TRAILING_NEWLINE = False # default filters, tests and namespace diff --git a/jinja2/environment.py b/jinja2/environment.py index 1b5dc40..fb04e64 100644 --- a/jinja2/environment.py +++ b/jinja2/environment.py @@ -140,6 +140,13 @@ class Environment(object): useful default for Linux and OS X systems as well as web applications. + `keep_trailing_newline` + Preserve the trailing newline when rendering templates. + The default is ``False``, which causes a single newline, + if present, to be stripped from the end of the template. + + .. versionadded:: 2.7 + `extensions` List of Jinja extensions to use. This can either be import paths as strings or extension classes. For more information have a @@ -225,6 +232,7 @@ class Environment(object): line_comment_prefix=LINE_COMMENT_PREFIX, trim_blocks=TRIM_BLOCKS, newline_sequence=NEWLINE_SEQUENCE, + keep_trailing_newline=KEEP_TRAILING_NEWLINE, extensions=(), optimized=True, undefined=Undefined, @@ -256,6 +264,7 @@ class Environment(object): self.line_comment_prefix = line_comment_prefix self.trim_blocks = trim_blocks self.newline_sequence = newline_sequence + self.keep_trailing_newline = keep_trailing_newline # runtime information self.undefined = undefined @@ -821,6 +830,7 @@ class Template(object): line_comment_prefix=LINE_COMMENT_PREFIX, trim_blocks=TRIM_BLOCKS, newline_sequence=NEWLINE_SEQUENCE, + keep_trailing_newline=KEEP_TRAILING_NEWLINE, extensions=(), optimized=True, undefined=Undefined, @@ -830,8 +840,8 @@ class Template(object): block_start_string, block_end_string, variable_start_string, variable_end_string, comment_start_string, comment_end_string, line_statement_prefix, line_comment_prefix, trim_blocks, - newline_sequence, frozenset(extensions), optimized, undefined, - finalize, autoescape, None, 0, False, None) + newline_sequence, keep_trailing_newline, frozenset(extensions), + optimized, undefined, finalize, autoescape, None, 0, False, None) return env.from_string(source, template_class=cls) @classmethod diff --git a/jinja2/ext.py b/jinja2/ext.py index 206756f..650ddd2 100644 --- a/jinja2/ext.py +++ b/jinja2/ext.py @@ -589,7 +589,9 @@ def babel_extract(fileobj, keywords, comment_tags, options): options.get('line_statement_prefix') or LINE_STATEMENT_PREFIX, options.get('line_comment_prefix') or LINE_COMMENT_PREFIX, getbool(options, 'trim_blocks', TRIM_BLOCKS), - NEWLINE_SEQUENCE, frozenset(extensions), + NEWLINE_SEQUENCE, + getbool(options, 'keep_trailing_newline', KEEP_TRAILING_NEWLINE), + frozenset(extensions), cache_size=0, auto_reload=False ) diff --git a/jinja2/lexer.py b/jinja2/lexer.py index 69865d0..0eb19f7 100644 --- a/jinja2/lexer.py +++ b/jinja2/lexer.py @@ -383,7 +383,8 @@ def get_lexer(environment): environment.line_statement_prefix, environment.line_comment_prefix, environment.trim_blocks, - environment.newline_sequence) + environment.newline_sequence, + environment.keep_trailing_newline) lexer = _lexer_cache.get(key) if lexer is None: lexer = Lexer(environment) @@ -426,6 +427,7 @@ class Lexer(object): block_suffix_re = environment.trim_blocks and '\\n?' or '' self.newline_sequence = environment.newline_sequence + self.keep_trailing_newline = environment.keep_trailing_newline # global lexing rules self.rules = { @@ -549,7 +551,14 @@ class Lexer(object): """This method tokenizes the text and returns the tokens in a generator. Use this method if you just want to tokenize a template. """ - source = '\n'.join(unicode(source).splitlines()) + source = unicode(source) + lines = source.splitlines() + if self.keep_trailing_newline and source: + for newline in ('\r\n', '\r', '\n'): + if source.endswith(newline): + lines.append('') + break + source = '\n'.join(lines) pos = 0 lineno = 1 stack = ['root'] diff --git a/jinja2/testsuite/lexnparse.py b/jinja2/testsuite/lexnparse.py index 77b76ec..3bf788e 100644 --- a/jinja2/testsuite/lexnparse.py +++ b/jinja2/testsuite/lexnparse.py @@ -82,6 +82,19 @@ class LexerTestCase(JinjaTestCase): result = tmpl.render() assert result.replace(seq, 'X') == '1X2X3X4' + def test_trailing_newline(self): + for keep in [True, False]: + env = Environment(keep_trailing_newline=keep) + for template,expected in [ + ('', {}), + ('no\nnewline', {}), + ('with\nnewline\n', {False: 'with\nnewline'}), + ('with\nseveral\n\n\n', {False: 'with\nseveral\n\n'}), + ]: + tmpl = env.from_string(template) + expect = expected.get(keep, template) + result = tmpl.render() + assert result == expect, (keep, template, result, expect) class ParserTestCase(JinjaTestCase): |