summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorW. Trevor King <wking@tremily.us>2013-01-11 08:23:24 -0500
committerW. Trevor King <wking@tremily.us>2013-01-11 08:47:30 -0500
commit7e912c6dfa7a17dd1334ecb1a8d57dd5722b633a (patch)
tree5072fa0f8dda217a8d26c5a56b11521b167f12c5
parent21a2010bf2768bc658e09666c2135063ce004efc (diff)
downloadjinja2-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--CHANGES2
-rw-r--r--docs/templates.rst4
-rw-r--r--jinja2/defaults.py1
-rw-r--r--jinja2/environment.py14
-rw-r--r--jinja2/ext.py4
-rw-r--r--jinja2/lexer.py13
-rw-r--r--jinja2/testsuite/lexnparse.py13
7 files changed, 45 insertions, 6 deletions
diff --git a/CHANGES b/CHANGES
index 6539c5f..43f5e8f 100644
--- a/CHANGES
+++ b/CHANGES
@@ -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):