summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/news.txt3
-rw-r--r--paste/util/template.py59
-rw-r--r--tests/test_template.txt11
3 files changed, 68 insertions, 5 deletions
diff --git a/docs/news.txt b/docs/news.txt
index 00274bb..e886faf 100644
--- a/docs/news.txt
+++ b/docs/news.txt
@@ -20,6 +20,9 @@ svn trunk
* ``egg:Paste#watch_threads`` will show tracebacks of each thread
under Python 2.5.
+* Made ``paste.util.template`` trim whitespace around statements that
+ are on their own line.
+
1.2.1
-----
diff --git a/paste/util/template.py b/paste/util/template.py
index 977a4e1..d76f6b3 100644
--- a/paste/util/template.py
+++ b/paste/util/template.py
@@ -358,7 +358,7 @@ def sub_html(content, **kw):
## Lexing and Parsing
############################################################
-def lex(s, name=None):
+def lex(s, name=None, trim_whitespace=True):
"""
Lex a string into chunks:
@@ -409,8 +409,59 @@ def lex(s, name=None):
part = s[last:]
if part:
chunks.append(part)
+ if trim_whitespace:
+ chunks = trim_lex(chunks)
return chunks
+statement_re = re.compile(r'^(?:if |elif |else |for |py:)')
+single_statements = ['endif', 'endfor', 'continue', 'break']
+trail_whitespace_re = re.compile(r'\n[\t ]*$')
+lead_whitespace_re = re.compile(r'^[\t ]*\n')
+
+def trim_lex(tokens):
+ r"""
+ Takes a lexed set of tokens, and removes whitespace when there is
+ a directive on a line by itself:
+
+ >>> tokens = lex('{{if x}}\nx\n{{endif}}\ny', trim_whitespace=False)
+ >>> tokens
+ [('if x', (1, 3)), '\nx\n', ('endif', (3, 3)), '\ny']
+ >>> trim_lex(tokens)
+ [('if x', (1, 3)), 'x\n', ('endif', (3, 3)), 'y']
+ """
+ for i in range(len(tokens)):
+ current = tokens[i]
+ if isinstance(tokens[i], basestring):
+ # we don't trim this
+ continue
+ item = current[0]
+ if not statement_re.search(item) and item not in single_statements:
+ continue
+ if not i:
+ prev = ''
+ else:
+ prev = tokens[i-1]
+ if i+1 >= len(tokens):
+ next = ''
+ else:
+ next = tokens[i+1]
+ if (not isinstance(next, basestring)
+ or not isinstance(prev, basestring)):
+ continue
+ if ((not prev or trail_whitespace_re.search(prev))
+ and (not next or lead_whitespace_re.search(next))):
+ if prev:
+ m = trail_whitespace_re.search(prev)
+ # +1 to leave the leading \n on:
+ prev = prev[:m.start()+1]
+ tokens[i-1] = prev
+ if next:
+ m = lead_whitespace_re.search(next)
+ next = next[m.end():]
+ tokens[i+1] = next
+ return tokens
+
+
def find_position(string, index):
"""Given a string and index, return (line, column)"""
leading = string[:index].splitlines()
@@ -547,9 +598,9 @@ def parse_one_cond(tokens, name, context):
'No {{endif}}',
position=pos, name=name)
if (isinstance(tokens[0], tuple)
- and tokens[0][0] == 'endif'
- or tokens[0][0].startswith('elif ')
- or tokens[0][0] == 'else'):
+ and (tokens[0][0] == 'endif'
+ or tokens[0][0].startswith('elif ')
+ or tokens[0][0] == 'else')):
return part, tokens
next, tokens = parse_expr(tokens, name, context)
content.append(next)
diff --git a/tests/test_template.txt b/tests/test_template.txt
index 9765011..a56049f 100644
--- a/tests/test_template.txt
+++ b/tests/test_template.txt
@@ -106,4 +106,13 @@ in Python, but it's more useful in templates generally)::
3 Banana
O:
4 orange
- \ No newline at end of file
+
+It will also strip out empty lines, when there is a line that only
+contains a directive/statement (if/for, etc)::
+
+ >>> sub('{{if 1}}\n{{x}}\n{{endif}}\n', x=0)
+ '0\n'
+ >>> sub('{{if 1}}x={{x}}\n{{endif}}\n', x=1)
+ 'x=1\n'
+ >>> sub('{{if 1}}\nx={{x}}\n{{endif}}\n', x=1)
+ 'x=1\n'