Commit 1816437f authored by Evan Simpson's avatar Evan Simpson

Optimized scoping, and added nocall:

parent 8661868c
...@@ -89,7 +89,7 @@ Page Template-specific implementation of TALES, with handlers ...@@ -89,7 +89,7 @@ Page Template-specific implementation of TALES, with handlers
for Python expressions, string literals, and paths. for Python expressions, string literals, and paths.
""" """
__version__='$Revision: 1.17 $'[11:-2] __version__='$Revision: 1.18 $'[11:-2]
import re, sys import re, sys
from TALES import Engine, CompilerError, _valid_name, NAME_RE, \ from TALES import Engine, CompilerError, _valid_name, NAME_RE, \
...@@ -108,7 +108,7 @@ def getEngine(): ...@@ -108,7 +108,7 @@ def getEngine():
def installHandlers(engine): def installHandlers(engine):
reg = engine.registerType reg = engine.registerType
pe = PathExpr pe = PathExpr
for pt in ('standard', 'path', 'exists'): for pt in ('standard', 'path', 'exists', 'nocall'):
reg(pt, pe) reg(pt, pe)
reg('string', StringExpr) reg('string', StringExpr)
reg('python', PythonExpr) reg('python', PythonExpr)
...@@ -171,50 +171,46 @@ class PathExpr: ...@@ -171,50 +171,46 @@ class PathExpr:
dp.reverse() dp.reverse()
return base, path, dp return base, path, dp
def _eval(self, (base, path, dp), econtext, securityManager, def _eval(self, econtext, securityManager,
list=list, isinstance=isinstance, StringType=type('')): list=list, isinstance=isinstance, StringType=type(''),
path = list(path) # Copy! render=render):
contexts = econtext.contexts vars = econtext.vars
var = contexts['var'] exists = 0
# Expand dynamic path parts from right to left. for base, path, dp in self._paths:
for i, varname in dp: path = list(path) # Copy!
val = var[varname] # Expand dynamic path parts from right to left.
if isinstance(val, StringType): for i, varname in dp:
path[i] = val val = vars[varname]
else: if isinstance(val, StringType):
# If the value isn't a string, assume it's a sequence path[i] = val
# of path names. else:
path[i:i+1] = list(val) # If the value isn't a string, assume it's a sequence
try: # of path names.
__traceback_info__ = base path[i:i+1] = list(val)
if base == 'CONTEXTS': try:
ob = contexts __traceback_info__ = base
else: if base == 'CONTEXTS':
has, ob = var.has_get(base) ob = econtext.contexts
if not has: else:
ob = contexts[base] ob = vars[base]
if path: if path:
return restrictedTraverse(ob, path, securityManager) ob = restrictedTraverse(ob, path, securityManager)
else: exists = 1
return ob
except (AttributeError, KeyError, TypeError, IndexError,
'Unauthorized'), e:
return Undefined(self._s, sys.exc_info())
def __call__(self, econtext):
securityManager = getSecurityManager()
for pathinfo in self._paths:
ob = self._eval(pathinfo, econtext, securityManager)
exists = not isinstance(ob, Undefined)
if exists:
# We're done
break break
except (AttributeError, KeyError, TypeError, IndexError,
'Unauthorized'), e:
ob = Undefined(self._s, sys.exc_info())
if self._name == 'exists': if self._name == 'exists':
# All we wanted to know is whether one of the paths exist. # All we wanted to know is whether one of the paths exist.
return exists return exists
if self._name == 'nocall' or isinstance(ob, StringType):
return ob
# Return the rendered object # Return the rendered object
return render(ob, econtext.contexts) return render(ob, vars)
def __call__(self, econtext):
return self._eval(econtext, getSecurityManager())
def __str__(self): def __str__(self):
return '%s expression "%s"' % (self._name, self._s) return '%s expression "%s"' % (self._name, self._s)
......
...@@ -86,7 +86,7 @@ ...@@ -86,7 +86,7 @@
"""Generic Python Expression Handler """Generic Python Expression Handler
""" """
__version__='$Revision: 1.2 $'[11:-2] __version__='$Revision: 1.3 $'[11:-2]
from string import strip, split, join, replace, lstrip from string import strip, split, join, replace, lstrip
...@@ -117,10 +117,10 @@ class PythonExpr: ...@@ -117,10 +117,10 @@ class PythonExpr:
def _bind_used_names(self, econtext): def _bind_used_names(self, econtext):
# Bind template variables # Bind template variables
names = {} names = {}
var = econtext.contexts['var'] vars = econtext.vars
getType = econtext._engine.getTypes().get getType = econtext._engine.getTypes().get
for vname in self._f_varnames: for vname in self._f_varnames:
has, val = var.has_get(vname) has, val = vars.has_get(vname)
if not has: if not has:
has = val = getType(vname) has = val = getType(vname)
if has: if has:
......
...@@ -87,7 +87,7 @@ ...@@ -87,7 +87,7 @@
An implementation of a generic TALES engine An implementation of a generic TALES engine
""" """
__version__='$Revision: 1.15 $'[11:-2] __version__='$Revision: 1.16 $'[11:-2]
import re, sys, ZTUtils import re, sys, ZTUtils
from MultiMapping import MultiMapping from MultiMapping import MultiMapping
...@@ -230,49 +230,49 @@ class Context: ...@@ -230,49 +230,49 @@ class Context:
contexts['nothing'] = None contexts['nothing'] = None
contexts['default'] = Default contexts['default'] = Default
# Keep track of what contexts get pushed as each scope begins. self.repeat_vars = rv = {}
self._ctxts_pushed = [] # Wrap this, as it is visible to restricted code
# These contexts will need to be pushed. contexts['repeat'] = rep = self._context_class(rv)
self._current_ctxts = {'local': 1, 'repeat': 1}
lv = self._context_class()
init_local = contexts.get('local', None)
if init_local:
lv._push(init_local)
contexts['local'] = lv
contexts['repeat'] = rep = self._context_class()
contexts['loop'] = rep # alias contexts['loop'] = rep # alias
contexts['global'] = gv = contexts.copy()
contexts['var'] = self._context_class(gv, lv) self.global_vars = gv = contexts.copy()
self.local_vars = lv = {}
self.vars = self._context_class(gv, lv)
# Keep track of what needs to be popped as each scope ends.
self._scope_stack = []
def beginScope(self): def beginScope(self):
oldctxts = self._current_ctxts self._scope_stack.append([self.local_vars.copy()])
self._ctxts_pushed.append(oldctxts)
self._current_ctxts = ctxts = {}
contexts = self.contexts
for ctxname in oldctxts.keys():
# Push fresh namespace on each local stack.
ctxts[ctxname] = ctx = {}
contexts[ctxname]._push(ctx)
def endScope(self): def endScope(self):
self._current_ctxts = ctxts = self._ctxts_pushed.pop() scope = self._scope_stack.pop()
# Pop the ones that were pushed at the beginning of the scope. self.local_vars = lv = scope[0]
contexts = self.contexts v = self.vars
for ctxname in ctxts.keys(): v._pop()
# Pop, then make sure there's no circular garbage v._push(lv)
contexts[ctxname]._pop().clear() # Pop repeat variables, if any
i = len(scope) - 1
while i:
name, value = scope[i]
if value is None:
del self.repeat_vars[name]
else:
self.repeat_vars[name] = value
i = i - 1
def setLocal(self, name, value): def setLocal(self, name, value):
self._current_ctxts['local'][name] = value self.local_vars[name] = value
def setGlobal(self, name, value): def setGlobal(self, name, value):
self.contexts['global'][name] = value self.global_vars[name] = value
def setRepeat(self, name, expr): def setRepeat(self, name, expr):
expr = self.evaluate(expr) expr = self.evaluate(expr)
it = self._engine.Iterator(name, expr, self) it = self._engine.Iterator(name, expr, self)
self._current_ctxts['repeat'][name] = it old_value = self.repeat_vars.get(name)
self._scope_stack[-1].append((name, old_value))
self.repeat_vars[name] = it
return it return it
def evaluate(self, expression, def evaluate(self, expression,
......
...@@ -89,7 +89,7 @@ Handler for Python expressions, using the pre-Python 2.1 restriction ...@@ -89,7 +89,7 @@ Handler for Python expressions, using the pre-Python 2.1 restriction
machinery from PythonScripts. machinery from PythonScripts.
""" """
__version__='$Revision: 1.2 $'[11:-2] __version__='$Revision: 1.3 $'[11:-2]
from AccessControl import getSecurityManager from AccessControl import getSecurityManager
from Products.PythonScripts.Guarded import _marker, \ from Products.PythonScripts.Guarded import _marker, \
...@@ -128,7 +128,7 @@ def call_with_ns(f, ns, arg=1): ...@@ -128,7 +128,7 @@ def call_with_ns(f, ns, arg=1):
td.this = None td.this = None
td._push(ns['request']) td._push(ns['request'])
td._push(InstanceDict(ns['here'], td)) td._push(InstanceDict(ns['here'], td))
td._push(ns['var']) td._push(ns)
try: try:
if arg==2: if arg==2:
return f(None, td) return f(None, td)
......
...@@ -88,7 +88,7 @@ ...@@ -88,7 +88,7 @@
Handler for Python expressions that uses the RestrictedPython package. Handler for Python expressions that uses the RestrictedPython package.
""" """
__version__='$Revision: 1.2 $'[11:-2] __version__='$Revision: 1.3 $'[11:-2]
from AccessControl import full_read_guard, full_write_guard, \ from AccessControl import full_read_guard, full_write_guard, \
safe_builtins, getSecurityManager safe_builtins, getSecurityManager
...@@ -133,7 +133,7 @@ def call_with_ns(f, ns, arg=1): ...@@ -133,7 +133,7 @@ def call_with_ns(f, ns, arg=1):
td.this = None td.this = None
td._push(ns['request']) td._push(ns['request'])
td._push(InstanceDict(ns['here'], td, full_read_guard)) td._push(InstanceDict(ns['here'], td, full_read_guard))
td._push(ns['var']) td._push(ns)
try: try:
if arg==2: if arg==2:
return f(None, td) return f(None, td)
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
</span> </span>
<dl> <dl>
<span tal:repeat="arg options/batch"> <span tal:repeat="arg options/batch">
<dt><span tal:replace="local/arg">??</span>.</dt> <dt><span tal:replace="arg">??</span>.</dt>
<dd>Argument <span tal:define="num arg/num" <dd>Argument <span tal:define="num arg/num"
tal:replace="string: $num" tal:replace="string: $num"
>99</span> was <span tal:replace="arg" >99</span> was <span tal:replace="arg"
......
...@@ -89,25 +89,25 @@ class TALESTests(unittest.TestCase): ...@@ -89,25 +89,25 @@ class TALESTests(unittest.TestCase):
def testVariables(self): def testVariables(self):
'''Test variables''' '''Test variables'''
ctxt = self.getContext() ctxt = self.getContext()
c = ctxt.contexts c = ctxt.vars
ctxt.beginScope() ctxt.beginScope()
ctxt.setLocal('v1', 1) ctxt.setLocal('v1', 1)
ctxt.setLocal('v2', 2) ctxt.setLocal('v2', 2)
assert c['var']['v1'] == 1, 'Variable "v1"' assert c['v1'] == 1, 'Variable "v1"'
ctxt.beginScope() ctxt.beginScope()
ctxt.setLocal('v1', 3) ctxt.setLocal('v1', 3)
ctxt.setGlobal('g', 1) ctxt.setGlobal('g', 1)
assert c['var']['v1'] == 3, 'Inner scope' assert c['v1'] == 3, 'Inner scope'
assert c['var']['v2'] == 2, 'Outer scope' assert c['v2'] == 2, 'Outer scope'
assert c['var']['g'] == 1, 'Global' assert c['g'] == 1, 'Global'
ctxt.endScope() ctxt.endScope()
assert c['var']['v1'] == 1, "Uncovered local" assert c['v1'] == 1, "Uncovered local"
assert c['var']['g'] == 1, "Global from inner scope" assert c['g'] == 1, "Global from inner scope"
ctxt.endScope() ctxt.endScope()
......
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