[Checkins] SVN: z3c.pt/trunk/ Added support for global defines.
Malthe Borch
mborch at gmail.com
Mon Aug 18 17:46:30 EDT 2008
Log message for revision 89982:
Added support for global defines.
Changed:
U z3c.pt/trunk/CHANGES.txt
U z3c.pt/trunk/src/z3c/pt/clauses.py
U z3c.pt/trunk/src/z3c/pt/codegen.py
U z3c.pt/trunk/src/z3c/pt/codegen.txt
U z3c.pt/trunk/src/z3c/pt/expressions.py
U z3c.pt/trunk/src/z3c/pt/generation.py
U z3c.pt/trunk/src/z3c/pt/genshi.txt
U z3c.pt/trunk/src/z3c/pt/template.py
U z3c.pt/trunk/src/z3c/pt/template.txt
U z3c.pt/trunk/src/z3c/pt/translation.py
U z3c.pt/trunk/src/z3c/pt/types.py
-=-
Modified: z3c.pt/trunk/CHANGES.txt
===================================================================
--- z3c.pt/trunk/CHANGES.txt 2008-08-18 21:44:21 UTC (rev 89981)
+++ z3c.pt/trunk/CHANGES.txt 2008-08-18 21:46:29 UTC (rev 89982)
@@ -4,6 +4,10 @@
Version 1.0dev
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+- Implemented TAL global defines. [malthe]
+
+- Added support for variables with global scope. [malthe]
+
- Curly braces may now be omitted in an expression interpolation if
the expression is just a variable name; this complies with the
Genshi syntax. [malthe]
Modified: z3c.pt/trunk/src/z3c/pt/clauses.py
===================================================================
--- z3c.pt/trunk/src/z3c/pt/clauses.py 2008-08-18 21:44:21 UTC (rev 89981)
+++ z3c.pt/trunk/src/z3c/pt/clauses.py 2008-08-18 21:46:29 UTC (rev 89982)
@@ -200,7 +200,7 @@
Tuple assignments:
>>> stream = CodeIO()
- >>> define = Define(['e', 'f'], pyexp("[1, 2]"))
+ >>> define = Define(types.declaration(('e', 'f')), pyexp("[1, 2]"))
>>> define.begin(stream)
>>> exec stream.getvalue()
>>> e == 1 and f == 2
@@ -243,56 +243,68 @@
1
"""
- def __init__(self, definition, expression):
- if not isinstance(definition, (list, tuple)):
- definition = (definition,)
+ def __init__(self, declaration, expression, dictionary=None):
+ if not isinstance(declaration, types.declaration):
+ declaration = types.declaration((declaration,))
- if len(definition) == 1:
- variable = definition[0]
+ if len(declaration) == 1:
+ variable = declaration[0]
else:
- variable = u"(%s,)" % ", ".join(definition)
+ variable = u"(%s,)" % ", ".join(declaration)
+ if dictionary is not None:
+ variable = "%s['%s'] = %s" % (dictionary, variable, variable)
+
self.assign = Assign(expression, variable)
- self.definitions = definition
+ self.declaration = declaration
+ self.dictionary = dictionary
def begin(self, stream):
- # save local variables already in in scope
- for var in self.definitions:
- temp = stream.save()
+ if self.declaration.global_scope:
+ # if the declaration belongs to a global scope, remove this
+ # symbol from previous scopes
+ for scope in stream.scope:
+ for variable in self.declaration:
+ if variable in scope:
+ scope.remove(variable)
+ else:
+ # save local variables already in in scope
+ for var in self.declaration:
+ temp = stream.save()
- # If we didn't set the variable in this scope already
- if var not in stream.scope[-1]:
+ # If we didn't set the variable in this scope already
+ if var not in stream.scope[-1]:
- # we'll check if it's set in one of the older scopes
- for scope in stream.scope[:-1]:
- if var in scope:
- # in which case we back it up
- stream.write('%s = %s' % (temp, var))
+ # we'll check if it's set in one of the older scopes
+ for scope in stream.scope[:-1]:
+ if var in scope:
+ # in which case we back it up
+ stream.write('%s = %s' % (temp, var))
- stream.scope[-1].add(var)
+ stream.scope[-1].add(var)
self.assign.begin(stream)
def end(self, stream):
self.assign.end(stream)
- # back come the variables that were already in scope in the
- # first place
- for var in reversed(self.definitions):
- temp = stream.restore()
+ if not self.declaration.global_scope:
+ # restore the variables that were previously in scope
+ for var in reversed(self.declaration):
+ temp = stream.restore()
- # If we set the variable in this scope already
- if var in stream.scope[-1]:
+ # If we set the variable in this scope already
+ if var in stream.scope[-1]:
- # we'll check if it's set in one of the older scopes
- for scope in stream.scope[:-1]:
- if var in scope:
- # in which case we restore it
- stream.write('%s = %s' % (var, temp))
- stream.scope[-1].remove(var)
- break
- else:
- stream.write("del %s" % var)
+ # we'll check if it's set in one of the older scopes
+ for scope in stream.scope[:-1]:
+ if var in scope:
+ # in which case we restore it
+ stream.write('%s = %s' % (var, temp))
+ stream.scope[-1].remove(var)
+ break
+ else:
+ stream.write("del %s" % var)
class Condition(object):
"""
Modified: z3c.pt/trunk/src/z3c/pt/codegen.py
===================================================================
--- z3c.pt/trunk/src/z3c/pt/codegen.py 2008-08-18 21:44:21 UTC (rev 89981)
+++ z3c.pt/trunk/src/z3c/pt/codegen.py 2008-08-18 21:46:29 UTC (rev 89982)
@@ -3,11 +3,22 @@
from transformer import ASTTransformer
+import __builtin__
CONSTANTS = frozenset(['False', 'True', 'None', 'NotImplemented', 'Ellipsis'])
UNDEFINED = object()
+def flatten(items):
+ """Flattens a potentially nested sequence into a flat list."""
+ retval = []
+ for item in items:
+ if isinstance(item, (frozenset, list, set, tuple)):
+ retval += flatten(item)
+ else:
+ retval.append(item)
+ return retval
+
class Lookup(object):
"""Abstract base class for variable lookup implementations."""
@@ -18,6 +29,7 @@
"""
return {
'_lookup_attr': cls.lookup_attr,
+ '_lookup_name': cls.lookup_name,
}
@classmethod
@@ -30,16 +42,99 @@
except (KeyError, TypeError):
raise e
+ @classmethod
+ def lookup_name(cls, data, name):
+ try:
+ return data[name]
+ except KeyError:
+ raise NameError(name)
+
class TemplateASTTransformer(ASTTransformer):
- def __init__(self):
+ """Concrete AST transformer that implements the AST transformations needed
+ for code embedded in templates.
+ """
+
+ def __init__(self, globals):
self.locals = [CONSTANTS]
+ self.locals.append(set(globals))
+ self.locals.append(set(dir(__builtin__)))
+ def visitConst(self, node):
+ if isinstance(node.value, str):
+ try: # If the string is ASCII, return a `str` object
+ node.value.decode('ascii')
+ except ValueError: # Otherwise return a `unicode` object
+ return ast.Const(node.value.decode('utf-8'))
+ return node
+
+ def visitAssName(self, node):
+ if len(self.locals) > 1:
+ if node.flags == 'OP_ASSIGN':
+ self.locals[-1].add(node.name)
+ else:
+ self.locals[-1].remove(node.name)
+ return node
+
+ def visitClass(self, node):
+ if len(self.locals) > 1:
+ self.locals[-1].add(node.name)
+ self.locals.append(set())
+ try:
+ return ASTTransformer.visitClass(self, node)
+ finally:
+ self.locals.pop()
+
+ def visitFor(self, node):
+ self.locals.append(set())
+ try:
+ return ASTTransformer.visitFor(self, node)
+ finally:
+ self.locals.pop()
+
+ def visitFunction(self, node):
+ if len(self.locals) > 1:
+ self.locals[-1].add(node.name)
+ self.locals.append(set(node.argnames))
+ try:
+ return ASTTransformer.visitFunction(self, node)
+ finally:
+ self.locals.pop()
+
+ def visitGenExpr(self, node):
+ self.locals.append(set())
+ try:
+ return ASTTransformer.visitGenExpr(self, node)
+ finally:
+ self.locals.pop()
+
+ def visitLambda(self, node):
+ self.locals.append(set(flatten(node.argnames)))
+ try:
+ return ASTTransformer.visitLambda(self, node)
+ finally:
+ self.locals.pop()
+
+ def visitListComp(self, node):
+ self.locals.append(set())
+ try:
+ return ASTTransformer.visitListComp(self, node)
+ finally:
+ self.locals.pop()
+
+ def visitName(self, node):
+ # If the name refers to a local inside a lambda, list comprehension, or
+ # generator expression, leave it alone
+ if node.name not in flatten(self.locals):
+ # Otherwise, translate the name ref into a context lookup
+ func_args = [ast.Name('_scope'), ast.Const(node.name)]
+ node = ast.CallFunc(ast.Name('_lookup_name'), func_args)
+ return node
+
def visitGetattr(self, node):
- """
- Allow fallback to dictionary lookup if attribute does not exist.
+ """Get attribute with fallback to dictionary lookup.
- Variables starting with an underscore are exempt.
-
+ Note: Variables starting with an underscore are exempt
+ (reserved for internal use).
"""
if hasattr(node.expr, 'name') and node.expr.name.startswith('_'):
@@ -52,16 +147,15 @@
class Suite(object):
__slots__ = ['code', '_globals']
- xform = TemplateASTTransformer
mode = 'exec'
- def __init__(self, source):
+ def __init__(self, source, globals=()):
"""Create the code object from a string."""
node = parse(source, self.mode)
# build tree
- transform = self.xform()
+ transform = TemplateASTTransformer(globals)
tree = transform.visit(node)
filename = tree.filename = '<script>'
Modified: z3c.pt/trunk/src/z3c/pt/codegen.txt
===================================================================
--- z3c.pt/trunk/src/z3c/pt/codegen.txt 2008-08-18 21:44:21 UTC (rev 89981)
+++ z3c.pt/trunk/src/z3c/pt/codegen.txt 2008-08-18 21:46:29 UTC (rev 89982)
@@ -17,8 +17,8 @@
>>> exec suite.code in suite._globals
Hello World!
-AST transformations
--------------------
+Syntax extension: Dictionary lookup using dot operator
+------------------------------------------------------
We allow attribute access to dictionary entries to minimize verbosity
in templates. It works by wrapping the get attribute nodes in a method
@@ -29,3 +29,12 @@
... assert a['b'] == a.b
... """)
>>> exec suite.code in suite._globals
+
+Syntax extension: Dynamic scoping
+---------------------------------
+
+ >>> suite = Suite("""\
+ ... _scope = {'a': 1}
+ ... assert a == 1
+ ... """)
+ >>> exec suite.code in suite._globals
Modified: z3c.pt/trunk/src/z3c/pt/expressions.py
===================================================================
--- z3c.pt/trunk/src/z3c/pt/expressions.py 2008-08-18 21:44:21 UTC (rev 89981)
+++ z3c.pt/trunk/src/z3c/pt/expressions.py 2008-08-18 21:46:29 UTC (rev 89982)
@@ -72,7 +72,7 @@
Single variable:
>>> declaration("variable")
- declaration('variable',)
+ declaration('variable')
Multiple variables:
@@ -165,13 +165,13 @@
Single define:
>>> definitions("variable expression")
- definitions((declaration('variable',), value('expression')),)
+ definitions((declaration('variable'), value('expression')),)
Multiple defines:
>>> definitions("variable1 expression1; variable2 expression2")
- definitions((declaration('variable1',), value('expression1')),
- (declaration('variable2',), value('expression2')))
+ definitions((declaration('variable1'), value('expression1')),
+ (declaration('variable2'), value('expression2')))
Tuple define:
@@ -179,35 +179,40 @@
definitions((declaration('variable1', 'variable2'),
value('(expression1, expression2)')),)
+ Global defines:
+
+ >>> definitions("global variable expression")
+ definitions((declaration('variable', global_scope=True), value('expression')),)
+
Space, the 'in' operator and '=' may be used to separate
variable from expression.
>>> definitions("variable in expression")
- definitions((declaration('variable',), value('expression')),)
+ definitions((declaration('variable'), value('expression')),)
>>> definitions("variable1 = expression1; variable2 = expression2")
- definitions((declaration('variable1',), value('expression1')),
- (declaration('variable2',), value('expression2')))
+ definitions((declaration('variable1'), value('expression1')),
+ (declaration('variable2'), value('expression2')))
>>> definitions("variable1=expression1; variable2=expression2")
- definitions((declaration('variable1',), value('expression1')),
- (declaration('variable2',), value('expression2')))
+ definitions((declaration('variable1'), value('expression1')),
+ (declaration('variable2'), value('expression2')))
A define clause that ends in a semicolon:
>>> definitions("variable expression;")
- definitions((declaration('variable',), value('expression')),)
+ definitions((declaration('variable'), value('expression')),)
A define clause with a trivial expression (we do allow this):
>>> definitions("variable")
- definitions((declaration('variable',), None),)
+ definitions((declaration('variable'), None),)
A proper define clause following one with a trivial expression:
>>> definitions("variable1 expression; variable2")
- definitions((declaration('variable1',), value('expression')),
- (declaration('variable2',), None))
+ definitions((declaration('variable1'), value('expression')),
+ (declaration('variable2'), None))
"""
@@ -216,6 +221,11 @@
defines = []
i = 0
while i < len(string):
+ global_scope = False
+ if string.startswith('global'):
+ global_scope = True
+ i += 6
+
while string[i] == ' ':
i += 1
@@ -238,6 +248,8 @@
else:
var = self.declaration(string[i:j])
+ var.global_scope = global_scope
+
# get expression
i = j + len(string) - j - len(string[j:].lstrip())
@@ -567,11 +579,11 @@
Semi-colon literal.
>>> definitions("variable part1;; part2")
- definitions((declaration('variable',), join('part1; part2',)),)
+ definitions((declaration('variable'), join('part1; part2',)),)
>>> definitions("variable1 part1;; part2; variable2 part3")
- definitions((declaration('variable1',), join('part1; part2',)),
- (declaration('variable2',), join('part3',)))
+ definitions((declaration('variable1'), join('part1; part2',)),
+ (declaration('variable2'), join('part3',)))
"""
Modified: z3c.pt/trunk/src/z3c/pt/generation.py
===================================================================
--- z3c.pt/trunk/src/z3c/pt/generation.py 2008-08-18 21:44:21 UTC (rev 89981)
+++ z3c.pt/trunk/src/z3c/pt/generation.py 2008-08-18 21:46:29 UTC (rev 89982)
@@ -18,7 +18,7 @@
\t_domain, _negotiate, _translate = generation.initialize_i18n()
\t_marker = generation.initialize_helpers()
\t_path = generation.initialize_traversal()
-
+\t_scope = {}
\t_target_language = _negotiate(_context, target_language)
%(code)s
\treturn _out.getvalue()
@@ -74,7 +74,7 @@
self.stream = CodeIO(indentation=1, indentation_string="\t")
# initialize variable scope
- self.stream.scope.append(set(('_out', '_write') + tuple(params)))
+ self.stream.scope.append(set(('_out', '_write', '_scope') + tuple(params)))
def __call__(self):
params = self.params
Modified: z3c.pt/trunk/src/z3c/pt/genshi.txt
===================================================================
--- z3c.pt/trunk/src/z3c/pt/genshi.txt 2008-08-18 21:44:21 UTC (rev 89981)
+++ z3c.pt/trunk/src/z3c/pt/genshi.txt 2008-08-18 21:46:29 UTC (rev 89982)
@@ -346,7 +346,7 @@
:: Both py:for and ${..} should be ignored in a comment, or both should be
handled.
- >>> print render_genshi("""\
+ >> print render_genshi("""\
... <div xmlns="http://www.w3.org/1999/xhtml"
... xmlns:py="http://genshi.edgewall.org">
... <!--
@@ -394,6 +394,12 @@
</script>
</div>
+:: XML Inclusions (XIncludes)
+Genshi supports inclusion of other templates using the XInclude
+specification.
+
+
+
.. _py:with specs: http://genshi.edgewall.org/wiki/Documentation/0.4.x/xml-templates.html#py-with
Modified: z3c.pt/trunk/src/z3c/pt/template.py
===================================================================
--- z3c.pt/trunk/src/z3c/pt/template.py 2008-08-18 21:44:21 UTC (rev 89981)
+++ z3c.pt/trunk/src/z3c/pt/template.py 2008-08-18 21:46:29 UTC (rev 89982)
@@ -46,8 +46,7 @@
default_expression=self.default_expression)
source, _globals = generator()
-
- suite = codegen.Suite(source)
+ suite = codegen.Suite(source, _globals.keys())
if self.cachedir:
self.registry.store(params, suite.code)
Modified: z3c.pt/trunk/src/z3c/pt/template.txt
===================================================================
--- z3c.pt/trunk/src/z3c/pt/template.txt 2008-08-18 21:44:21 UTC (rev 89981)
+++ z3c.pt/trunk/src/z3c/pt/template.txt 2008-08-18 21:46:29 UTC (rev 89982)
@@ -138,6 +138,10 @@
... xmlns:metal="http://xml.zope.org/namespaces/metal">
... <div metal:define-macro="greeting">
... Hello, ${name}!
+ ... <span tal:define="global name 'earth'">
+ ... Hello, ${name}!
+ ... </span>
+ ... Hello, ${name}!
... </div>
... </div>""")
@@ -147,7 +151,9 @@
... xmlns:metal="http://xml.zope.org/namespaces/metal">
... <div tal:define="name 'world'">
... <div metal:use-macro="template1.macros['greeting']" />
- ... </div>
+ ... Hello, ${name}!
+ ... </div>
+ ... Hello, ${name}!
... </div>""")
>>> print template2(template1=template1)
@@ -155,9 +161,15 @@
<div>
<div>
Hello, world!
+ <span>
+ Hello, earth!
+ </span>
+ Hello, earth!
</div>
<BLANKLINE>
- </div>
+ Hello, world!
+ </div>
+ Hello, earth!
</div>
Error handling
Modified: z3c.pt/trunk/src/z3c/pt/translation.py
===================================================================
--- z3c.pt/trunk/src/z3c/pt/translation.py 2008-08-18 21:44:21 UTC (rev 89981)
+++ z3c.pt/trunk/src/z3c/pt/translation.py 2008-08-18 21:46:29 UTC (rev 89982)
@@ -23,6 +23,7 @@
metal_slot_prefix = '_fill'
metal_variable = '_metal'
macro_variable = '_macro'
+ scope_variable = '_scope'
def start(self, stream):
self._stream = stream
@@ -85,8 +86,12 @@
# variable definitions
if self.node.define is not None:
- for variables, expression in self.node.define:
- _.append(clauses.Define(variables, expression))
+ for declaration, expression in self.node.define:
+ if declaration.global_scope:
+ _.append(clauses.Define(
+ declaration, expression, self.scope_variable))
+ else:
+ _.append(clauses.Define(declaration, expression))
# macro method
for element in tuple(self):
@@ -223,7 +228,7 @@
subclauses = []
subclauses.append(clauses.Define(
- ('_out', '_write'),
+ types.declaration(('_out', '_write')),
types.value('generation.initialize_stream()')))
subclauses.append(clauses.Visit(element))
subclauses.append(clauses.Assign(
@@ -238,7 +243,8 @@
itertools.chain(*self.stream.scope))+
tuple("%s=%s" % kwarg for kwarg in kwargs))
- _.append(clauses.Write(types.value("%s(%s)" % (self.metal_variable, arguments))))
+ _.append(clauses.Write(
+ types.value("%s(%s)" % (self.metal_variable, arguments))))
# translate body
elif self.node.translate is not None:
@@ -261,7 +267,7 @@
subclauses = []
subclauses.append(clauses.Define(
- ('_out', '_write'),
+ types.declaration(('_out', '_write')),
types.value('generation.initialize_stream()')))
subclauses.append(clauses.Visit(element))
subclauses.append(clauses.Assign(
Modified: z3c.pt/trunk/src/z3c/pt/types.py
===================================================================
--- z3c.pt/trunk/src/z3c/pt/types.py 2008-08-18 21:44:21 UTC (rev 89981)
+++ z3c.pt/trunk/src/z3c/pt/types.py 2008-08-18 21:46:29 UTC (rev 89982)
@@ -14,8 +14,13 @@
return 'join'+tuple.__repr__(self)
class declaration(tuple):
+ global_scope = False
+
def __repr__(self):
- return 'declaration'+tuple.__repr__(self)
+ items = map(repr, self)
+ if self.global_scope:
+ items.append('global_scope=%s' % repr(self.global_scope))
+ return 'declaration(%s)' % ', '.join(items)
class mapping(tuple):
def __repr__(self):
More information about the Checkins
mailing list