From 3dabe23c995fca20b5460a12ade3549f7f1810fe Mon Sep 17 00:00:00 2001 From: Mike Bayer <mike_mp@zzzcomputing.com> Date: Wed, 2 May 2007 01:01:45 +0000 Subject: [PATCH] - control lines, i.e. % lines, support backslashes to continue long lines (#32) - fixed single "#" comments in docs --- CHANGES | 4 +++- doc/build/content/caching.txt | 2 +- doc/build/content/defs.txt | 6 +++--- doc/build/content/unicode.txt | 4 ++-- lib/mako/ast.py | 2 +- lib/mako/lexer.py | 2 +- test/lexer.py | 11 +++++++++++ test/template.py | 10 ++++++++++ 8 files changed, 32 insertions(+), 9 deletions(-) diff --git a/CHANGES b/CHANGES index 0e62f4c..26539d3 100644 --- a/CHANGES +++ b/CHANGES @@ -16,7 +16,9 @@ - the Template returned by html_error_template now defaults to output_encoding=sys.getdefaultencoding(), encoding_errors='htmlentityreplace' [ticket:37] - +- control lines, i.e. % lines, support backslashes to continue long + lines (#32) + 0.1.5 - AST expression generation - added in just about everything expression-wise from the AST module [ticket:26] diff --git a/doc/build/content/caching.txt b/doc/build/content/caching.txt index 8aae6d2..e6dee41 100644 --- a/doc/build/content/caching.txt +++ b/doc/build/content/caching.txt @@ -34,5 +34,5 @@ The options available are: ${next.body()} - # rest of template + ## rest of template diff --git a/doc/build/content/defs.txt b/doc/build/content/defs.txt index 43e84ac..fb81ec2 100644 --- a/doc/build/content/defs.txt +++ b/doc/build/content/defs.txt @@ -95,7 +95,7 @@ Assigning to a name inside of a def declares that name as local to the scope of x = 10 %> <%def name="somedef()"> - # error ! + ## error ! somedef, x is ${x} <% x = 27 @@ -199,7 +199,7 @@ Produces (whitespace formatted): You don't have to stick to calling just the `body()` function. The caller can define any number of callables, allowing the `<%call>` tag to produce whole layouts: <%def name="layout()"> - # a layout def + ## a layout def <div class="mainlayout"> <div class="header"> ${caller.header()} @@ -213,7 +213,7 @@ You don't have to stick to calling just the `body()` function. The caller can d </div> </%def> - # calls the layout def + ## calls the layout def <%call expr="layout"> <%def name="header()"> I am the header diff --git a/doc/build/content/unicode.txt b/doc/build/content/unicode.txt index 9f92a9c..6daba9d 100644 --- a/doc/build/content/unicode.txt +++ b/doc/build/content/unicode.txt @@ -48,11 +48,11 @@ looks something like this: That is, **the output of all expressions is run through the `unicode` builtin**. This is the default setting, and can be modified to expect various encodings. The `unicode` step serves both the purpose of rendering non-string expressions into strings (such as integers or objects which contain `__str()__` methods), and to ensure that the final output stream is constructed as a unicode object. The main implication of this is that **any raw bytestrings that contain an encoding other than ascii must first be decoded to a Python unicode object**. It means you can't say this: - ${"voix m’a réveillé."} # error ! + ${"voix m’a réveillé."} ## error ! You must instead say this: - ${u"voix m’a réveillé."} # OK ! + ${u"voix m’a réveillé."} ## OK ! Similarly, if you are reading data from a file, or returning data from some object that is returning a Python bytestring containing a non-ascii encoding, you have to explcitly decode to unicode first, such as: diff --git a/lib/mako/ast.py b/lib/mako/ast.py index 0d00f30..db2c316 100644 --- a/lib/mako/ast.py +++ b/lib/mako/ast.py @@ -139,7 +139,7 @@ class PythonFragment(PythonCode): etc. """ def __init__(self, code, **exception_kwargs): - m = re.match(r'^(\w+)(?:\s+(.*?))?:$', code.strip()) + m = re.match(r'^(\w+)(?:\s+(.*?))?:$', code.strip(), re.S) if not m: raise exceptions.CompileException("Fragment '%s' is not a partial control statement" % code, **exception_kwargs) (keyword, expr) = m.group(1,2) diff --git a/lib/mako/lexer.py b/lib/mako/lexer.py index dafa10d..34e7c1a 100644 --- a/lib/mako/lexer.py +++ b/lib/mako/lexer.py @@ -283,7 +283,7 @@ class Lexer(object): return False def match_control_line(self): - match = self.match(r"(?<=^)[\t ]*(%|##)[\t ]*([^\r\n]*)(?:\r?\n|\Z)", re.M) + match = self.match(r"(?<=^)[\t ]*(%|##)[\t ]*((?:(?:\\r?\n)|[^\r\n])*)(?:\r?\n|\Z)", re.M) if match: operator = match.group(1) text = match.group(2) diff --git a/test/lexer.py b/test/lexer.py index fd8ea75..e088a50 100644 --- a/test/lexer.py +++ b/test/lexer.py @@ -290,6 +290,17 @@ text text la la nodes = Lexer(template).parse() assert repr(nodes) == r"""TemplateNode({}, [Text(u'\n\n\n', (1, 1)), ControlLine(u'for', u"for file in requestattr['toc'].filenames:", False, (4, 1)), Text(u' x\n', (5, 1)), ControlLine(u'for', u'endfor', True, (6, 1))])""" + def test_long_control_lines(self): + template = \ + """ + % for file in \\ + requestattr['toc'].filenames: + x + % endfor + """ + nodes = Lexer(template).parse() + assert repr(nodes) == r"""TemplateNode({}, [Text(u'\n', (1, 1)), ControlLine(u'for', u"for file in \\\n requestattr['toc'].filenames:", False, (2, 1)), Text(u' x\n', (4, 1)), ControlLine(u'for', u'endfor', True, (5, 1)), Text(u' ', (6, 1))])""" + def test_unmatched_control(self): template = """ diff --git a/test/template.py b/test/template.py index c3d2b12..0d931c6 100644 --- a/test/template.py +++ b/test/template.py @@ -243,6 +243,16 @@ class ControlTest(unittest.TestCase): "yes x has test" ] + def test_multiline_control(self): + t = Template(""" + % for x in \\ + [y for y in [1,2,3]]: + ${x} + % endfor +""") + #print t.code + assert flatten_result(t.render()) == "1 2 3" + class GlobalsTest(unittest.TestCase): def test_globals(self): t= Template(""" -- GitLab