changeset 601:9ae986bcba9a

Simplify implementation of `py:with` directive by compiling to a `Suite`, instead of manually breaking up the statement and compiling each part to an `Expression`. Also, the first line of code in a `Suite` is now stored as the "function name" of the bytecode, so that it shows up in tracebacks.
author cmlenz
date Wed, 22 Aug 2007 21:50:46 +0000
parents 0d802a7e3630
children 509b3a5e765e
files genshi/template/directives.py genshi/template/eval.py genshi/template/interpolation.py genshi/template/markup.py
diffstat 4 files changed, 31 insertions(+), 37 deletions(-) [+]
line wrap: on
line diff
--- a/genshi/template/directives.py
+++ b/genshi/template/directives.py
@@ -19,7 +19,7 @@
 from genshi.path import Path
 from genshi.template.base import TemplateRuntimeError, TemplateSyntaxError, \
                                  EXPR, _apply_directives
-from genshi.template.eval import Expression, _parse
+from genshi.template.eval import Expression, Suite, _parse
 
 __all__ = ['AttrsDirective', 'ChooseDirective', 'ContentDirective',
            'DefDirective', 'ForDirective', 'IfDirective', 'MatchDirective',
@@ -680,30 +680,26 @@
       <span>42 7 52</span>
     </div>
     """
-    __slots__ = ['vars']
+    __slots__ = ['suite']
 
     def __init__(self, value, template, namespaces=None, lineno=-1, offset=-1):
         Directive.__init__(self, None, template, namespaces, lineno, offset)
-        self.vars = []
-        value = value.strip()
         try:
-            ast = _parse(value, 'exec').node
-            for node in ast.nodes:
-                if isinstance(node, compiler.ast.Discard):
-                    continue
-                elif not isinstance(node, compiler.ast.Assign):
-                    raise TemplateSyntaxError('only assignment allowed in '
-                                              'value of the "with" directive',
-                                              template.filepath, lineno, offset)
-                self.vars.append(([_assignment(n) for n in node.nodes],
-                                  Expression(node.expr, template.filepath,
-                                             lineno, lookup=template.lookup)))
+            self.suite = Suite(value, template.filepath, lineno,
+                               lookup=template.lookup)
         except SyntaxError, err:
             err.msg += ' in expression "%s" of "%s" directive' % (value,
                                                                   self.tagname)
             raise TemplateSyntaxError(err, template.filepath, lineno,
                                       offset + (err.offset or 0))
 
+        for node in self.suite.ast.node.nodes:
+            if not isinstance(node, (compiler.ast.Discard,
+                                     compiler.ast.Assign)):
+                raise TemplateSyntaxError('only assignment allowed in value of '
+                                          'the "with" directive',
+                                          template.filepath, lineno, offset)
+
     def attach(cls, template, stream, value, namespaces, pos):
         if type(value) is dict:
             value = value.get('vars')
@@ -714,10 +710,7 @@
     def __call__(self, stream, ctxt, directives):
         frame = {}
         ctxt.push(frame)
-        for targets, expr in self.vars:
-            value = expr.evaluate(ctxt)
-            for assign in targets:
-                assign(frame, value)
+        self.suite.execute(ctxt)
         for event in _apply_directives(stream, ctxt, directives):
             yield event
         ctxt.pop()
--- a/genshi/template/eval.py
+++ b/genshi/template/eval.py
@@ -23,6 +23,7 @@
     from sets import ImmutableSet as frozenset
     from sets import Set as set
 import sys
+from textwrap import dedent
 
 from genshi.core import Markup
 from genshi.template.base import TemplateRuntimeError
@@ -53,7 +54,8 @@
             self.source = source
             node = _parse(source, mode=self.mode)
         else:
-            assert isinstance(source, ast.Node)
+            assert isinstance(source, ast.Node), \
+                'Expected string or AST node, but got %r' % source
             self.source = '?'
             if self.mode == 'eval':
                 node = ast.Expression(source)
@@ -360,6 +362,14 @@
 
 
 def _parse(source, mode='eval'):
+    source = source.strip()
+    if mode == 'exec':
+        lines = [line.expandtabs() for line in source.splitlines()]
+        first = lines[0]
+        rest = dedent('\n'.join(lines[1:])).rstrip()
+        if first.rstrip().endswith(':') and not rest[0].isspace():
+            rest = '\n'.join(['    %s' % line for line in rest.splitlines()])
+        source = '\n'.join([first, rest])
     if isinstance(source, unicode):
         source = '\xef\xbb\xbf' + source.encode('utf-8')
     return parse(source, mode)
@@ -378,10 +388,14 @@
 
     if mode == 'eval':
         gen = ExpressionCodeGenerator(tree)
-        name = '<Expression %s>' % (repr(source or '?'))
+        name = '<Expression %r>' % (source or '?')
     else:
         gen = ModuleCodeGenerator(tree)
-        name = '<Suite>'
+        lines = source.splitlines()
+        extract = lines[0]
+        if len(lines) > 1:
+            extract += ' ...'
+        name = '<Suite %r>' % (extract)
     gen.optimized = True
     code = gen.getCode()
 
--- a/genshi/template/interpolation.py
+++ b/genshi/template/interpolation.py
@@ -73,7 +73,7 @@
             if chunk:
                 try:
                     expr = Expression(chunk.strip(), pos[0], pos[1],
-                                     lookup=lookup)
+                                      lookup=lookup)
                     yield EXPR, expr, tuple(pos)
                 except SyntaxError, err:
                     raise TemplateSyntaxError(err, filepath, pos[1],
--- a/genshi/template/markup.py
+++ b/genshi/template/markup.py
@@ -15,7 +15,6 @@
 
 from itertools import chain
 import sys
-from textwrap import dedent
 
 from genshi.core import Attrs, Namespace, Stream, StreamEventKind
 from genshi.core import START, END, START_NS, END_NS, TEXT, PI, COMMENT
@@ -197,19 +196,7 @@
                     raise TemplateSyntaxError('Python code blocks not allowed',
                                               self.filepath, *pos[1:])
                 try:
-                    # As Expat doesn't report whitespace between the PI target
-                    # and the data, we have to jump through some hoops here to
-                    # get correctly indented Python code
-                    # Unfortunately, we'll still probably not get the line
-                    # number quite right
-                    lines = [line.expandtabs() for line in data[1].splitlines()]
-                    first = lines[0]
-                    rest = dedent('\n'.join(lines[1:])).rstrip()
-                    if first.rstrip().endswith(':') and not rest[0].isspace():
-                        rest = '\n'.join(['    ' + line for line
-                                          in rest.splitlines()])
-                    source = '\n'.join([first, rest])
-                    suite = Suite(source, self.filepath, pos[1],
+                    suite = Suite(data[1], self.filepath, pos[1],
                                   lookup=self.lookup)
                 except SyntaxError, err:
                     raise TemplateSyntaxError(err, self.filepath,
Copyright (C) 2012-2017 Edgewall Software