1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
|
"""Utilties for computing DTML (restricted Python) Expressions
This module exports everything you need to set up and call DTML expressions,
including pickle-able (but not Persistent) Expression and Name lookup
classes. The general usage pattern looks like this::
from Products.ZPatterns.Expressions import *
md = NamespaceStack() # make a namespace stack
i = InstanceDict(object, md) # prepare an object for pushing onto stack
md._push(i) # push it on the stack
getSecurityManager().addContext(self) # add owner mask/proxy roles
try:
# compute the result
result = Expression(someString).eval(md)
finally:
getSecurityManager().removeContext(self) # remove security context
md._pop() # remove pushed object
Of course, if you need to place other data on the stack, your actual code
will vary. Also, it is more likely that you will store
'Expression(someString)' as an attribute of your own objects, and simply
call its '.eval()' method when you need it, in order to avoid excessive
recompiling of the same expression.
Note: The 'pushProxy(ob)' and 'popProxy(ob)' routines are simple synonyms for
'getSecurityManager().addContext(ob)' and
'getSecurityManager().removeContext(ob)', respectively. They were originally
created to mask the differences between Zope 2.1.x and 2.2.x, and so should
be avoided when writing new code.
"""
from ExtensionClass import Base
from ComputedAttribute import ComputedAttribute
from DocumentTemplate.DT_Util import Eval, TemplateDict, InstanceDict
from AccessControl import getSecurityManager
class NamespaceStack(TemplateDict):
"""A DTML-style namespace stack, complete with Zope security
To use, just create one with 'NamespaceStack()', then use its '_push()' and
'_pop()' methods to add/remove mapping objects. If you need to push a
regular object instance (ala DTML's "with" tag), use::
stack._push(InstanceDict(object,stack))"""
# Called by Zope only
def validate(self, inst, parent, name, value, md):
return getSecurityManager().validate(inst, parent, name, value)
def pushProxy(ob):
"""Push an object onto the security manager context stack
DEPRECATED - use 'getSecurityManager().addContext(ob)' instead"""
getSecurityManager().addContext(ob)
def popProxy(ob):
"""Pop an object from the security manager context stack
DEPRECATED - use 'getSecurityManager().removeContext(ob)' instead"""
getSecurityManager().removeContext(ob)
try:
# temporary backward hack for 2.3
from DocumentTemplate.DT_Util import expr_globals
def Eval(expr,expr_globals=expr_globals,Eval=Eval):
return Eval(expr,expr_globals)
del expr_globals
except ImportError:
pass # not needed in 2.4
class Expression(Base):
"""DTML (restricted Python) Expression object that can be pickled
To use, create with 'Expression("some string")'. You can then save the
created object as an attribute of any 'Persistent' object, and it will be
properly pickled, without writing any actual Python bytecode to the ZODB.
The secret is that the '_v_expr' computed attribute automatically recompiles
the expression whenever it's needed, but caches it while the 'Expression' is
in memory to avoid unnecessary re-compiling.
To compute the result of the expression, call its 'eval()' method, passing
in a 'NamespaceStack' object."""
def __init__(self, expr):
"""Create the expression object. 'expr' is a string."""
self.expr = expr
# Private: computed attribute for compiled form of expression
def _v_expr(self, Eval=Eval):
self._v_expr = Eval(self.expr)
return self._v_expr
_v_expr = ComputedAttribute(_v_expr)
# Private: returns pickle-able state, minus compiled expression
def __getstate__(self):
d = self.__dict__.copy()
if d.has_key('_v_expr'):
del d['_v_expr']
return d
# Private: load pickle state
def __setstate__(self, state):
d = self.__dict__
d.clear(); d.update(state)
def eval(self,mapping):
"""Return the result of computing the expression in the context of 'mapping'"""
return self._v_expr.eval(mapping)
class Name:
"""Object that does a DTML-style name lookup, with optional call of the
retrieved object"""
def __init__(self,name,call=1):
"""Create an object with the same interface as an 'Expression', but
which just looks up a name, and calls it if 'call==1'."""
self.name, self.call = name, call
def eval(self,mapping):
"""Return the result of looking up/calling the name from 'mapping'.
If the object was created with 'call==1', call it before returning it."""
return mapping.getitem(self.name,self.call)
del Eval, TemplateDict, Base, ComputedAttribute
|