Skip to content

Commit

Permalink
fix (most) problems revealed by the Products.CMFPlone tests
Browse files Browse the repository at this point in the history
  • Loading branch information
d-maurer committed Mar 23, 2020
1 parent 8bb3e24 commit 6d4fef1
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 1 deletion.
37 changes: 36 additions & 1 deletion src/Products/PageTemplates/engine.py
Expand Up @@ -12,6 +12,7 @@
from chameleon.astutil import Static
from chameleon.astutil import Symbol
from chameleon.codegen import template
from chameleon.exc import ExpressionError
from chameleon.tal import RepeatDict
from chameleon.zpt.template import Macros

Expand All @@ -25,6 +26,8 @@
from zope.pagetemplate.engine import ZopeBaseEngine
from zope.pagetemplate.interfaces import IPageTemplateEngine
from zope.pagetemplate.interfaces import IPageTemplateProgram
from zope.tales.expressions import PathExpr
from zope.tales.expressions import SubPathExpr

from .Expressions import SecureModuleImporter

Expand Down Expand Up @@ -115,7 +118,39 @@ def __init__(self, type, expression, zt_engine):
self.type = type
self.expression = expression
# compile to be able to report errors
_compile_zt_expr(type, expression, engine=zt_engine)
compiler_error = zt_engine.getCompilerError()
try:
zt_expr = _compile_zt_expr(type, expression, engine=zt_engine)
except compiler_error as e:
raise ExpressionError(str(e), self.expression)
if self.type == "path" and "$" in self.expression \
and isinstance(zt_expr, PathExpr):
# the ``chameleon`` template engine has a really curious
# implementation of global ``$`` interpolation
# (see ``chameleon.compiler.Interpolator``):
# when it sees ``${``, it starts with the largest
# substring starting at this position and ending in ``}``
# and tries to generate code for it. If this fails, it
# retries with the second largest such substring, etc.
# Of course, this fails with ``zope.tales`` ``path`` expressions
# where almost any character is syntactically legal.
# Thus, it happily generates code for e.g.
# ``d/a} ${d/b`` (resulting from ``${d/a} ${d/b}``)
# but its evaluation will fail (with high likelyhood).
# We use a heuristics here to handle many (but not all)
# resulting problems: forbid ``$`` in ``SubPathExpr``s.
for se in zt_expr._subexprs:
# dereference potential evaluation method
se = getattr(se, "__self__", se)
# we assume below that expressions other than
# ``SubPathExpr`` have flagged out ``$`` use already
# we know that this assumption is wrong in some cases
if isinstance(se, SubPathExpr):
for pe in se._compiled_path:
if isinstance(pe, tuple): # standard path
for spe in pe:
if "$" in spe:
raise ExpressionError("$ unsupported", spe)

def __call__(self, target, c_engine):
return template(
Expand Down
@@ -0,0 +1,6 @@
<html>
<head></head>
<body>
<p tal:define="d python:{'a': 'A', 'b': 'B'}">${d/a} ${d/b}</p>
</body>
</html>
@@ -0,0 +1,6 @@
<html>
<head></head>
<body>
<p>A B</p>
</body>
</html>
13 changes: 13 additions & 0 deletions src/Products/PageTemplates/tests/testHTMLTests.py
Expand Up @@ -15,6 +15,7 @@

from six import text_type

from chameleon.exc import ExpressionError
import zope.component.testing
from AccessControl import SecurityManager
from AccessControl.SecurityManagement import noSecurityManager
Expand Down Expand Up @@ -182,3 +183,15 @@ def testBooleanAttributesAndDefault(self):
# "boolean" attribute (e.g. 'selected', 'disabled', etc.) can
# be used together with 'default'.
self.assert_expected(self.folder.t, 'BooleanAttributesAndDefault.html')

def testInterpolationInContent(self):
# the chameleon template engine supports ``${path}``
# interpolations not only as part of ``string`` expressions
# but globally
self.assert_expected(self.folder.t, 'InterpolationInContent.html')

def testBadExpression(self):
t = self.folder.t
t.write("<p tal:define='p a//b' />")
with self.assertRaises(ExpressionError):
t()

0 comments on commit 6d4fef1

Please sign in to comment.