# HG changeset patch
# User cmlenz
# Date 1187819446 0
# Node ID 9ae986bcba9a1dc40cae0348b33299c6e30676ce
# Parent 0d802a7e363002d097746853f34b7c741d7b0513
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.
diff --git a/genshi/template/directives.py b/genshi/template/directives.py
--- 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 @@
42 7 52
"""
- __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()
diff --git a/genshi/template/eval.py b/genshi/template/eval.py
--- 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 = '' % (repr(source or '?'))
+ name = '' % (source or '?')
else:
gen = ModuleCodeGenerator(tree)
- name = ''
+ lines = source.splitlines()
+ extract = lines[0]
+ if len(lines) > 1:
+ extract += ' ...'
+ name = '' % (extract)
gen.optimized = True
code = gen.getCode()
diff --git a/genshi/template/interpolation.py b/genshi/template/interpolation.py
--- 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],
diff --git a/genshi/template/markup.py b/genshi/template/markup.py
--- 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,