Commit 56e64abe authored by Evan Simpson's avatar Evan Simpson

Refactor Path expressions, make them support other expression types as the...

Refactor Path expressions, make them support other expression types as the final alternate, and support Iterator.first() and last().
parent 26e2a24a
......@@ -17,11 +17,11 @@ Page Template-specific implementation of TALES, with handlers
for Python expressions, string literals, and paths.
"""
__version__='$Revision: 1.29 $'[11:-2]
__version__='$Revision: 1.30 $'[11:-2]
import re, sys
from TALES import Engine, CompilerError, _valid_name, NAME_RE, \
TALESError, Undefined, Default
TALESError, Undefined, Default, _parse_expr
from string import strip, split, join, replace, lstrip
from Acquisition import aq_base, aq_inner, aq_parent
......@@ -30,7 +30,8 @@ _engine = None
def getEngine():
global _engine
if _engine is None:
_engine = Engine()
from PathIterator import Iterator
_engine = Engine(Iterator)
installHandlers(_engine)
_engine._nocatch = (TALESError, 'Redirect')
return _engine
......@@ -71,6 +72,9 @@ else:
else:
return f(ns)
Undefs = (Undefined, AttributeError, KeyError,
TypeError, IndexError, Unauthorized)
def render(ob, ns):
"""
Calls the object, possibly a document template, or just returns it if
......@@ -91,75 +95,98 @@ def render(ob, ns):
raise
return ob
class PathExpr:
def __init__(self, name, expr, engine):
self._s = expr
self._name = name
self._paths = map(self._prepPath, split(expr, '|'))
def _prepPath(self, path):
path = split(strip(path), '/')
base = path.pop(0)
class SubPathExpr:
def __init__(self, path):
self._path = path = split(strip(path), '/')
self._base = base = path.pop(0)
if not _valid_name(base):
raise CompilerError, 'Invalid variable name "%s"' % base
# Parse path
dp = []
self._dp = dp = []
for i in range(len(path)):
e = path[i]
if e[:1] == '?' and _valid_name(e[1:]):
dp.append((i, e[1:]))
dp.reverse()
return base, path, dp
def _eval(self, econtext, securityManager,
list=list, isinstance=isinstance, StringType=type(''),
render=render):
def _eval(self, econtext,
list=list, isinstance=isinstance, StringType=type('')):
vars = econtext.vars
exists = 0
more_paths = len(self._paths)
for base, path, dp in self._paths:
more_paths = more_paths - 1
# Expand dynamic path parts from right to left.
if dp:
path = list(path) # Copy!
for i, varname in dp:
val = vars[varname]
if isinstance(val, StringType):
path[i] = val
else:
# If the value isn't a string, assume it's a sequence
# of path names.
path[i:i+1] = list(val)
try:
__traceback_info__ = base
if base == 'CONTEXTS':
ob = econtext.contexts
path = self._path
if self._dp:
path = list(path) # Copy!
for i, varname in self._dp:
val = vars[varname]
if isinstance(val, StringType):
path[i] = val
else:
ob = vars[base]
if isinstance(ob, DeferWrapper):
ob = ob()
if path:
ob = restrictedTraverse(ob, path, securityManager)
exists = 1
# If the value isn't a string, assume it's a sequence
# of path names.
path[i:i+1] = list(val)
__traceback_info__ = base = self._base
if base == 'CONTEXTS':
ob = econtext.contexts
else:
ob = vars[base]
if isinstance(ob, DeferWrapper):
ob = ob()
if path:
ob = restrictedTraverse(ob, path, getSecurityManager())
return ob
class PathExpr:
def __init__(self, name, expr, engine):
self._s = expr
self._name = name
paths = split(expr, '|')
self._subexprs = []
add = self._subexprs.append
for i in range(len(paths)):
path = lstrip(paths[i])
if _parse_expr(path):
# This part is the start of another expression type,
# so glue it back together and compile it.
add(engine.compile(lstrip(join(paths[i:], '|'))))
break
except Undefined:
if self._name != 'exists' and not more_paths:
raise
except (AttributeError, KeyError, TypeError, IndexError,
Unauthorized), e:
if self._name != 'exists' and not more_paths:
raise Undefined(self._s, sys.exc_info())
add(SubPathExpr(path)._eval)
def _exists(self, econtext):
for expr in self._subexprs:
try:
expr(econtext)
except Undefs:
pass
else:
return 1
return 0
def _eval(self, econtext,
isinstance=isinstance, StringType=type(''), render=render):
for expr in self._subexprs[:-1]:
# Try all but the last subexpression, skipping undefined ones
try:
ob = expr(econtext)
except Undefs:
pass
else:
break
else:
# On the last subexpression allow exceptions through, but
# wrap ones that indicate that the subexpression was undefined
try:
ob = self._subexprs[-1](econtext)
except Undefs[1:]:
raise Undefined(self._s, sys.exc_info())
if self._name == 'exists':
# All we wanted to know is whether one of the paths exist.
return exists
if self._name == 'nocall' or isinstance(ob, StringType):
return ob
# Return the rendered object
return render(ob, vars)
return render(ob, econtext.vars)
def __call__(self, econtext):
return self._eval(econtext, getSecurityManager())
if self._name == 'exists':
return self._exists(econtext)
return self._eval(econtext)
def __str__(self):
return '%s expression %s' % (self._name, `self._s`)
......@@ -247,21 +274,21 @@ class DeferExpr:
def restrictedTraverse(self, path, securityManager,
get=getattr, has=hasattr, N=None, M=[]):
i = 0
REQUEST = {'path': path}
REQUEST['TraversalRequestNameStack'] = path = path[:] # Copy!
if not path[0]:
# If the path starts with an empty string, go to the root first.
self = self.getPhysicalRoot()
if not securityManager.validateValue(self):
raise Unauthorized, name
i = 1
REQUEST={'TraversalRequestNameStack': path}
path.pop(0)
path.reverse()
validate = securityManager.validate
object = self
while i < len(path):
__traceback_info__ = (path, i)
name = path[i]
i = i + 1
while path:
__traceback_info__ = REQUEST
name = path.pop()
if name[0] == '_':
# Never allowed in a URL.
......
......@@ -15,7 +15,7 @@
HTML- and XML-based template objects using TAL, TALES, and METAL.
"""
__version__='$Revision: 1.19 $'[11:-2]
__version__='$Revision: 1.20 $'[11:-2]
import os, sys, traceback, pprint
from TAL.TALParser import TALParser
......@@ -40,6 +40,7 @@ class PageTemplate(Base):
expand = 1
_v_errors = ()
_v_warnings = ()
id = '(unknown)'
_text = ''
_error_start = '<!-- Page Template Diagnostics'
......
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""Path Iterator
A TALES Iterator with the ability to use first() and last() on
subpaths of elements.
"""
__version__='$Revision: 1.1 $'[11:-2]
import TALES
from Expressions import restrictedTraverse, Undefs, getSecurityManager
class Iterator(TALES.Iterator):
def __bobo_traverse__(self, REQUEST, name):
if name in ('first', 'last'):
path = REQUEST['TraversalRequestNameStack']
names = list(path)
del path[:]
names.reverse()
return getattr(self, name)(names)
return getattr(self, name)
def same_part(self, name, ob1, ob2):
if name is None:
return ob1 == ob2
if isinstance(name, type('')):
name = split(name, '/')
name = filter(None, name)
securityManager = getSecurityManager()
try:
ob1 = restrictedTraverse(ob1, name, securityManager)
ob2 = restrictedTraverse(ob2, name, securityManager)
except Undefs:
return 0
return ob1 == ob2
<html>
<body tal:define="ztu modules/ZTUtils;b python:ztu.Batch(range(10), 5)">
<p tal:repeat="n b">
Batch 1: item=<span tal:replace="n">n</span>
</p>
<p tal:repeat="n b/next">
Batch 2: item=<span tal:replace="n">n</span>
</p>
</body>
</html>
......@@ -5,10 +5,14 @@
<p tal:content="x | nil">2</p>
<p tal:content="python:nil or x">3</p>
<p tal:content="y/z | x">4</p>
<p tal:content="y/z | string:X">4</p>
<p tal:content="y/z | python:'|AXE|'[2]">4</p>
<p tal:content="y/z | x | nil">5</p>
<p tal:attributes="name nil">Z</p>
<p tal:attributes="name y/z | nil">Z</p>
<p tal:attributes="name y/z | string:">Z</p>
<p tal:attributes="name y/z | python:'||'[:0]">Z</p>
<p tal:attributes="name y/z | nothing">Z</p>
<p tal:on-error="python:str(error.value)" tal:content="a/b | c/d">Z</p>
......
<html>
<body tal:define="objects python:[
{'name': 'fred', 'legs': 2},
{'name': 'wilma', 'legs': 2},
{'name': 'dino', 'legs': 4},
]">
<tal:block repeat="ob objects">
<h4 tal:condition="repeat/ob/first/legs">Legs:
<span tal:replace="ob/legs">1</span></h4>
<p tal:content="ob/name">Name</p>
<hr tal:condition="repeat/ob/last/legs" />
</tal:block>
</body>
</html>
<html>
<body>
<p>
Batch 1: item=0
</p>
<p>
Batch 1: item=1
</p>
<p>
Batch 1: item=2
</p>
<p>
Batch 1: item=3
</p>
<p>
Batch 1: item=4
</p>
<p>
Batch 2: item=5
</p>
<p>
Batch 2: item=6
</p>
<p>
Batch 2: item=7
</p>
<p>
Batch 2: item=8
</p>
<p>
Batch 2: item=9
</p>
</body>
</html>
......@@ -6,7 +6,11 @@
<p>X</p>
<p>X</p>
<p>X</p>
<p>X</p>
<p>X</p>
<p name="">Z</p>
<p name="">Z</p>
<p name="">Z</p>
<p name="">Z</p>
<p>Z</p>
......
<html>
<body>
<h4>Legs:
2</h4>
<p>fred</p>
<p>wilma</p>
<hr >
<h4>Legs:
4</h4>
<p>dino</p>
<hr >
</body>
</html>
......@@ -58,6 +58,13 @@ class HTMLTests(unittest.TestCase):
def tearDown(self):
SecurityManager.setSecurityPolicy( self.oldPolicy )
def assert_expected(self, t, fname, *args, **kwargs):
t.write(util.read_input(fname))
assert not t._v_errors, 'Template errors: %s' % t._v_errors
expect = util.read_output(fname)
out = apply(t, args, kwargs)
util.check_html(expect, out)
def getProducts(self):
return [
{'description': 'This is the tee for those who LOVE Zope. '
......@@ -71,86 +78,49 @@ class HTMLTests(unittest.TestCase):
]
def check1(self):
laf = self.folder.laf
laf.write(util.read_input('TeeShopLAF.html'))
expect = util.read_output('TeeShopLAF.html')
util.check_html(expect, laf())
self.assert_expected(self.folder.laf, 'TeeShopLAF.html')
def check2(self):
self.folder.laf.write(util.read_input('TeeShopLAF.html'))
t = self.folder.t
t.write(util.read_input('TeeShop2.html'))
expect = util.read_output('TeeShop2.html')
out = t(getProducts=self.getProducts)
util.check_html(expect, out)
self.assert_expected(self.folder.t, 'TeeShop2.html',
getProducts=self.getProducts)
def check3(self):
self.folder.laf.write(util.read_input('TeeShopLAF.html'))
t = self.folder.t
t.write(util.read_input('TeeShop1.html'))
expect = util.read_output('TeeShop1.html')
out = t(getProducts=self.getProducts)
util.check_html(expect, out)
self.assert_expected(self.folder.t, 'TeeShop1.html',
getProducts=self.getProducts)
def checkSimpleLoop(self):
t = self.folder.t
t.write(util.read_input('Loop1.html'))
expect = util.read_output('Loop1.html')
out = t()
util.check_html(expect, out)
self.assert_expected(self.folder.t, 'Loop1.html')
def checkFancyLoop(self):
self.assert_expected(self.folder.t, 'Loop2.html')
def checkGlobalsShadowLocals(self):
t = self.folder.t
t.write(util.read_input('GlobalsShadowLocals.html'))
expect = util.read_output('GlobalsShadowLocals.html')
out = t()
util.check_html(expect, out)
self.assert_expected(self.folder.t, 'GlobalsShadowLocals.html')
def checkStringExpressions(self):
t = self.folder.t
t.write(util.read_input('StringExpression.html'))
expect = util.read_output('StringExpression.html')
out = t()
util.check_html(expect, out)
self.assert_expected(self.folder.t, 'StringExpression.html')
def checkReplaceWithNothing(self):
t = self.folder.t
t.write(util.read_input('CheckNothing.html'))
expect = util.read_output('CheckNothing.html')
out = t()
util.check_html(expect, out)
self.assert_expected(self.folder.t, 'CheckNothing.html')
def checkWithXMLHeader(self):
t = self.folder.t
t.write(util.read_input('CheckWithXMLHeader.html'))
expect = util.read_output('CheckWithXMLHeader.html')
out = t()
util.check_html(expect, out)
self.assert_expected(self.folder.t, 'CheckWithXMLHeader.html')
def checkNotExpression(self):
t = self.folder.t
t.write(util.read_input('CheckNotExpression.html'))
expect = util.read_output('CheckNotExpression.html')
out = t()
util.check_html(expect, out)
self.assert_expected(self.folder.t, 'CheckNotExpression.html')
def checkPathNothing(self):
t = self.folder.t
t.write(util.read_input('CheckPathNothing.html'))
expect = util.read_output('CheckPathNothing.html')
out = t()
util.check_html(expect, out)
self.assert_expected(self.folder.t, 'CheckPathNothing.html')
def checkPathAlt(self):
t = self.folder.t
t.write(util.read_input('CheckPathAlt.html'))
expect = util.read_output('CheckPathAlt.html')
out = t()
util.check_html(expect, out)
self.assert_expected(self.folder.t, 'CheckPathAlt.html')
def checkBatchIteration(self):
self.assert_expected(self.folder.t, 'CheckBatchIteration.html')
def test_suite():
return unittest.makeSuite(HTMLTests, 'check')
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment