File: Expressions.py

package info (click to toggle)
zope-zpatterns 0.4.3p2-2
  • links: PTS
  • area: main
  • in suites: woody
  • size: 476 kB
  • ctags: 814
  • sloc: python: 2,817; ansic: 310; makefile: 52; sh: 39
file content (140 lines) | stat: -rw-r--r-- 5,032 bytes parent folder | download | duplicates (2)
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