[Zope-Checkins] CVS: Zope2 - DT_In.py:1.50 DT_Let.py:1.8 DT_Return.py:1.3 DT_String.py:1.41 DT_Util.py:1.74 DT_Var.py:1.39 DT_With.py:1.12 DocumentTemplate.py:1.11 __init__.py:1.15 cDocumentTemplate.c:1.36 pDocumentTemplate.py:1.28

shane@digicool.com shane@digicool.com
Fri, 27 Apr 2001 16:28:14 -0400 (EDT)


Update of /cvs-repository/Zope2/lib/python/DocumentTemplate
In directory korak:/tmp/cvs-serv17686/lib/python/DocumentTemplate

Modified Files:
	DT_In.py DT_Let.py DT_Return.py DT_String.py DT_Util.py 
	DT_Var.py DT_With.py DocumentTemplate.py __init__.py 
	cDocumentTemplate.c pDocumentTemplate.py 
Log Message:
Merged RestrictedPythonBranch!



--- Updated File DT_In.py in package Zope2 --
--- DT_In.py	2001/04/27 18:07:09	1.49
+++ DT_In.py	2001/04/27 20:27:39	1.50
@@ -405,8 +405,9 @@
 __rcs_id__='$Id$'
 __version__='$Revision$'[11:-2]
 
+import sys
 from DT_Util import ParseError, parse_params, name_param, str
-from DT_Util import render_blocks, InstanceDict, ValidationError, VSEval, expr_globals
+from DT_Util import render_blocks, InstanceDict, ValidationError, Eval
 from string import find, atoi, join, split, lower
 import re
 from DT_InSV import sequence_variables, opt
@@ -445,10 +446,10 @@
             if sort=='sequence-item': self.sort=''
 
         if has_key('sort_expr'):
-            self.sort_expr=VSEval.Eval(args['sort_expr'], expr_globals)
+            self.sort_expr=Eval(args['sort_expr'])
 
         if has_key('reverse_expr'):
-            self.reverse_expr=VSEval.Eval(args['reverse_expr'], expr_globals)
+            self.reverse_expr=Eval(args['reverse_expr'])
 
         if has_key('reverse'):
             self.reverse=args['reverse']
@@ -596,7 +597,7 @@
             else:
                 result = []
                 append=result.append
-                validate=md.validate
+                read_guard = md.read_guard
                 for index in range(first,end):
                     # preset
                     kw['previous-sequence']= 0
@@ -625,18 +626,19 @@
                         except: pass
         
                     if index==last: kw['sequence-end']=1
-
-                    client=sequence[index]
 
-                    if validate is not None:
-                        try: vv=validate(sequence,sequence,None,client,md)
-                        except: vv=0
-                        if not vv:
+                    if read_guard is not None:
+                        try: client = read_guard(sequence)[index]
+                        except ValidationError, vv:
                             if (params.has_key('skip_unauthorized') and
                                 params['skip_unauthorized']):
                                 if index==first: kw['sequence-start']=0
                                 continue
-                            raise ValidationError, index
+                            tb = sys.exc_info()[2]
+                            raise ValidationError, '(item %s): %s' % (
+                                index, vv), tb
+                    else:
+                        client = sequence[index]
 
                     kw['sequence-index']=index
                     if type(client)==TupleType and len(client)==2:
@@ -654,6 +656,7 @@
                 result=join(result, '')
 
         finally:
+            tb = None
             if cache: pop()
             pop()
 
@@ -709,20 +712,21 @@
         try:
                 result = []
                 append=result.append
-                validate=md.validate
+                read_guard = md.read_guard
                 for index in range(l):
                     if index==last: kw['sequence-end']=1
-                    client=sequence[index]
-
-                    if validate is not None:
-                        try: vv=validate(sequence,sequence,None,client,md)
-                        except: vv=0
-                        if not vv:
+                    if read_guard is not None:
+                        try: client = read_guard(sequence)[index]
+                        except ValidationError, vv:
                             if (self.args.has_key('skip_unauthorized') and
                                 self.args['skip_unauthorized']):
                                 if index==1: kw['sequence-start']=0
                                 continue
-                            raise ValidationError, index
+                            tb = sys.exc_info()[2]
+                            raise ValidationError, '(item %s): %s' % (
+                                index, vv), tb
+                    else:
+                        client = sequence[index]
 
                     kw['sequence-index']=index
                     if type(client)==TupleType and len(client)==2:
@@ -738,6 +742,7 @@
                 result=join(result, '')
 
         finally:
+            tb = None
             if cache: pop()
             pop()
 
@@ -841,7 +846,6 @@
 def nocase(str1, str2):
     return cmp(lower(str1), lower(str2))
 
-import sys
 if sys.modules.has_key("locale"): # only if locale is already imported
     from locale import strcoll
 

--- Updated File DT_Let.py in package Zope2 --
--- DT_Let.py	2001/04/27 18:07:10	1.7
+++ DT_Let.py	2001/04/27 20:27:39	1.8
@@ -112,7 +112,7 @@
    as desired.
 ''' 
 
-from DT_Util import render_blocks, Eval, expr_globals, ParseError,  strip
+from DT_Util import render_blocks, Eval, ParseError, strip
 from DT_Util import str # Probably needed due to hysterical pickles.
 import re
 
@@ -132,7 +132,7 @@
             if expr[:1]=='"' and expr[-1:]=='"' and len(expr) > 1:
 				# expr shorthand
                 expr=expr[1:-1]
-                try: args[i] = name, Eval(expr, expr_globals).eval
+                try: args[i] = name, Eval(expr).eval
                 except SyntaxError, v:
                     m,(huh,l,c,src) = v
                     raise ParseError, (
@@ -174,12 +174,12 @@
         value='"%s"' % mo1.group(3)
         l=len(mo1.group(1))
     else:
-        if not text or not strip(text): return result
+        if not text or not text.strip(): return result
         raise ParseError, ('invalid parameter: "%s"' % text, tag)
 
     result.append((name,value))
 
-    text=strip(text[l:])
+    text=text[l:].strip()
     if text: return apply(parse_let_params,(text,result,tag),parms)
     else: return result
 

--- Updated File DT_Return.py in package Zope2 --
--- DT_Return.py	2001/04/27 18:07:10	1.2
+++ DT_Return.py	2001/04/27 20:27:39	1.3
@@ -84,8 +84,8 @@
 ##############################################################################
 __version__='$Revision$'[11:-2]
 
-from DT_Util import parse_params, name_param, html_quote, str
-import  string, sys
+from DT_Util import parse_params, name_param, str
+import string, sys
 from string import find, split, join, atoi, rfind
 
 class ReturnTag: 

--- Updated File DT_String.py in package Zope2 --
--- DT_String.py	2001/04/27 18:07:10	1.40
+++ DT_String.py	2001/04/27 20:27:39	1.41
@@ -1,4 +1,3 @@
-
 ##############################################################################
 # 
 # Zope Public License (ZPL) Version 1.0
@@ -506,7 +505,7 @@
             if globals: push(globals)
             if mapping:
                 push(mapping)
-            md.validate=self.validate
+            md.read_guard=self.read_guard
             if client is not None:
                 if type(client)==type(()):
                     md.this=client[-1]
@@ -551,8 +550,8 @@
             if pushed: md._pop(pushed) # Get rid of circular reference!
             md.level=level # Restore previous level
 
-    validate__roles__=()
-    validate=None
+    read_guard__roles__=()
+    read_guard=None
 
     def __str__(self):
         return self.read()

--- Updated File DT_Util.py in package Zope2 --
--- DT_Util.py	2001/04/27 18:07:11	1.73
+++ DT_Util.py	2001/04/27 20:27:39	1.74
@@ -85,175 +85,79 @@
 '''$Id$''' 
 __version__='$Revision$'[11:-2]
 
-import  string, math, os
-import re
-from string import strip, join, atoi, lower, split, find
-import VSEval
+import re, os
+from string import lower
+from RestrictedPython.Guards import safe_builtins
+from RestrictedPython.Utilities import utility_builtins
+from RestrictedPython.Eval import RestrictionCapableEval
 
+LIMITED_BUILTINS = 1
+
 str=__builtins__['str'] # Waaaaa, waaaaaaaa needed for pickling waaaaa
 
 ParseError='Document Template Parse Error'
 ValidationError='Unauthorized'
 
-
-def html_quote(v, name='(Unknown name)', md={},
-               character_entities=(
-                       (('&'),    '&'),
-                       (('<'),    '&lt;' ),
-                       (('>'),    '&gt;' ),
-                       (('"'),    '&quot;'))): #"
-        text=str(v)
-        for re,name in character_entities:
-            if find(text, re) >= 0: text=join(split(text,re),name)
-        return text
-
 def int_param(params,md,name,default=0, st=type('')):
     try: v=params[name]
     except: v=default
     if v:
-        try: v=atoi(v)
+        try: v=int(v)
         except:
             v=md[v]
-            if type(v) is st: v=atoi(v)
+            if type(v) is st: v=int(v)
     return v or 0
 
-_marker=[]
-
-def careful_getattr(md, inst, name, default=_marker):
-    
-    if name[:1]!='_':
-
-        # Try to get the attribute normally so that we don't
-        # accidentally acquire when we shouldn't.
-        try: v=getattr(inst, name)
-        except:
-            if default is not _marker:
-                return default
-            raise
-
-        validate=md.validate
-
-        if validate is None: return v
-
-        if hasattr(inst,'aq_acquire'):
-            return inst.aq_acquire(name, validate, md)
-
-        if validate(inst,inst,name,v,md): return v
-
-    raise ValidationError, name
-
-def careful_hasattr(md, inst, name):
-    v=getattr(inst, name, _marker)
-    if v is not _marker:
-        try: 
-            if name[:1]!='_':
-                validate=md.validate                
-                if validate is None: return 1
-    
-                if hasattr(inst,'aq_acquire'):
-                    inst.aq_acquire(name, validate, md)
-                    return 1
-    
-                if validate(inst,inst,name,v,md): return 1
-        except: pass
-    return 0
-
-def careful_getitem(md, mapping, key):
-    v=mapping[key]
-
-    if type(v) is type(''): return v # Short-circuit common case
-
-    validate=md.validate
-    if validate is None or validate(mapping,mapping,None,v,md): return v
-    raise ValidationError, key
-
-def careful_getslice(md, seq, *indexes):
-    v=len(indexes)
-    if v==2:
-        v=seq[indexes[0]:indexes[1]]
-    elif v==1:
-        v=seq[indexes[0]:]
-    else: v=seq[:]
-
-    if type(seq) is type(''): return v # Short-circuit common case
-
-    validate=md.validate
-    if validate is not None:
-        for e in v:
-            if not validate(seq,seq,None,e,md):
-                raise ValidationError, 'unauthorized access to slice member'
-
-    return v
-
-def careful_range(md, iFirst, *args):
-    # limited range function from Martijn Pieters
-    RANGELIMIT = 1000
-    if not len(args):
-        iStart, iEnd, iStep = 0, iFirst, 1
-    elif len(args) == 1:
-        iStart, iEnd, iStep = iFirst, args[0], 1
-    elif len(args) == 2:
-        iStart, iEnd, iStep = iFirst, args[0], args[1]
-    else:
-        raise AttributeError, 'range() requires 1-3 int arguments'
-    if iStep == 0: raise ValueError, 'zero step for range()'
-    iLen = int((iEnd - iStart) / iStep)
-    if iLen < 0: iLen = 0
-    if iLen >= RANGELIMIT: raise ValueError, 'range() too large'
-    return range(iStart, iEnd, iStep)
-
-import string, math, whrandom
-
 try:
     import ExtensionClass
     from cDocumentTemplate import InstanceDict, TemplateDict, render_blocks
 except: from pDocumentTemplate import InstanceDict, TemplateDict, render_blocks
 
-
-d=TemplateDict.__dict__
-for name in ('None', 'abs', 'chr', 'divmod', 'float', 'hash', 'hex', 'int',
-             'len', 'max', 'min', 'oct', 'ord', 'round', 'str'):
-    d[name]=__builtins__[name]
-d['string']=string
-d['math']=math
-d['whrandom']=whrandom
-
-def careful_pow(self, x, y, z):
-    if not z: raise ValueError, 'pow(x, y, z) with z==0'
-    return pow(x,y,z)
 
-d['pow']=careful_pow
-
-try:
-    import random
-    d['random']=random
-except: pass
+functype = type(int_param)
+class NotBindable:
+    # Used to prevent TemplateDict from trying to bind to functions.
+    def __init__(self, f):
+        self.__call__ = f
+
+d = TemplateDict.__dict__
+for name, f in safe_builtins.items() + utility_builtins.items():
+    if type(f) is functype:
+        d[name] = NotBindable(f)
+    else:
+        d[name] = f
 
-try:
-    import DateTime
-    d['DateTime']=DateTime.DateTime
-except: pass
+if LIMITED_BUILTINS:
+    # Replace certain builtins with limited versions.
+    from RestrictedPython.Limits import limited_builtins
+    for name, f in limited_builtins.items():
+        if type(f) is functype:
+            d[name] = NotBindable(f)
+        else:
+            d[name] = f
 
-def test(self, *args):
-    l=len(args)
-    for i in range(1, l, 2):
-        if args[i-1]: return args[i]
+# The functions below are meant to bind to the TemplateDict.
 
-    if l%2: return args[-1]
+_marker = []  # Create a new marker object.
 
-d['test']=test
+def careful_getattr(md, inst, name, default=_marker):
+    read_guard = md.read_guard
+    if read_guard is not None:
+        inst = read_guard(inst)
+    if default is _marker:
+        return getattr(inst, name)
+    else:
+        return getattr(inst, name, default)
 
-def obsolete_attr(self, inst, name, md):
-    return careful_getattr(md, inst, name)
+def careful_hasattr(md, inst, name):
+    read_guard = md.read_guard
+    if read_guard is not None:
+        inst = read_guard(inst)
+    return hasattr(inst, name)
 
-d['attr']=obsolete_attr
 d['getattr']=careful_getattr
 d['hasattr']=careful_hasattr
-d['range']=careful_range
 
-#class namespace_:
-#    __allow_access_to_unprotected_subobjects__=1
-
 def namespace(self, **kw):
     """Create a tuple consisting of a single instance whose attributes are
     provided as keyword arguments."""
@@ -273,66 +177,51 @@
     else:
         vbase = getattr(v, 'aq_base', v)
         if callable(vbase):
-            if getattr(vbase, 'isDocTemp', 0):
-                v = v(None, self)
-            else:
-                v = v()
+            try:
+                if getattr(vbase, 'isDocTemp', 0):
+                    v = v(None, self)
+                else:
+                    v = v()
+            except AttributeError, n:
+                if n != '__call__':
+                    raise
     return v
 
 d['render']=render
 
-def reorder(self, s, with=None, without=()):
-    if with is None: with=s
-    d={}
-    tt=type(())
-    for i in s:
-        if type(i) is tt and len(i)==2: k, v = i
-        else:                           k= v = i
-        d[k]=v
-    r=[]
-    a=r.append
-    h=d.has_key
-
-    for i in without:
-        if type(i) is tt and len(i)==2: k, v = i
-        else:                           k= v = i
-        if h(k): del d[k]
-        
-    for i in with:
-        if type(i) is tt and len(i)==2: k, v = i
-        else:                           k= v = i
-        if h(k):
-            a((k,d[k]))
-            del d[k]
-
-    return r
-
-d['reorder']=reorder
-
-
-expr_globals={
-    '__builtins__':{},
-    '__guarded_mul__':      VSEval.careful_mul,
-    '__guarded_getattr__':  careful_getattr,
-    '__guarded_getitem__':  careful_getitem,
-    '__guarded_getslice__': careful_getslice,
-    }
 
-class Eval(VSEval.Eval):
-    
-    def eval(self, mapping):
-        d={'_vars': mapping, '_': mapping}
-        code=self.code
-        globals=self.globals
+class Eval(RestrictionCapableEval):
+
+    def eval(self, md):
+        guard = getattr(md, 'read_guard', None)
+        if guard is not None:
+            self.prepRestrictedCode()
+            code = self.rcode
+            d = {'_': md, '_vars': md,
+                 '_read_': guard, '__builtins__': None}
+        else:
+            self.prepUnrestrictedCode()
+            code = self.ucode
+            d = {'_': md, '_vars': md}
+        d.update(self.globals)
+        has_key = d.has_key
         for name in self.used:
             __traceback_info__ = name
-            try: d[name]=mapping.getitem(name,0)
+            try:
+                if not has_key(name):
+                    d[name] = md.getitem(name, 0)
             except KeyError:
-                if name=='_getattr':
-                    d['__builtins__']=globals
-                    exec compiled_getattr in d
-
-        return eval(code,globals,d)
+                # Swallow KeyErrors since the expression
+                # might not actually need the name.  If it
+                # does need the name, a NameError will occur.
+                pass
+        return eval(code, d)
+
+    def __call__(self, **kw):
+        # Never used?
+        md = TemplateDict()
+        md._push(kw)
+        return self.eval(md)
 
 
 def name_param(params,tag='',expr=0, attr='name', default_unnamed=1):
@@ -354,7 +243,7 @@
                 if used('expr'):
                     raise ParseError, ('two exprs given', tag)
                 v=v[1:-1]
-                try: expr=Eval(v, expr_globals)
+                try: expr=Eval(v)
                 except SyntaxError, v:
                     raise ParseError, (
                         '<strong>Expression (Python) Syntax error</strong>:'
@@ -384,7 +273,7 @@
         return params[attr]
     elif expr and used('expr'):
         name=params['expr']
-        expr=Eval(name, expr_globals)
+        expr=Eval(name)
         return name, expr
         
     raise ParseError, ('No %s given' % attr, tag)
@@ -521,7 +410,7 @@
         else: result['']=name
         return apply(parse_params,(text[l:],result),parms)
     else:
-        if not text or not strip(text): return result
+        if not text or not text.strip(): return result
         raise ParseError, ('invalid parameter: "%s"' % text, tag)
     
     if not parms.has_key(name):
@@ -536,6 +425,6 @@
             
     result[name]=value
 
-    text=strip(text[l:])
+    text=text[l:].strip()
     if text: return apply(parse_params,(text,result),parms)
     else: return result

--- Updated File DT_Var.py in package Zope2 --
--- DT_Var.py	2001/04/27 18:07:11	1.38
+++ DT_Var.py	2001/04/27 20:27:39	1.39
@@ -220,11 +220,15 @@
 __rcs_id__='$Id$'
 __version__='$Revision$'[11:-2]
 
-from DT_Util import parse_params, name_param, html_quote, str
+from DT_Util import parse_params, name_param, str
 import re, string, sys
 from string import find, split, join, atoi, rfind
 from urllib import quote, quote_plus
+from cgi import escape
 
+def html_quote(v, name='(Unknown name)', md={}):
+    return escape(str(v), 1)
+
 class Var: 
     name='var'
     expr=None
@@ -322,7 +326,7 @@
 
         if have_arg('size'):
             size=args['size']
-            try: size=atoi(size)
+            try: size=int(size)
             except: raise 'Document Error',(
                 '''a <code>size</code> attribute was used in a <code>var</code>
                 tag with a non-integer value.''')
@@ -426,7 +430,7 @@
     'collection-length': len_format,
     'structured-text': structured_text,
 
-    # The rest are depricated:
+    # The rest are deprecated:
     'sql-quote': sql_quote,
     'html-quote': html_quote,
     'url-quote': url_quote,

--- Updated File DT_With.py in package Zope2 --
--- DT_With.py	2000/05/11 18:54:14	1.11
+++ DT_With.py	2001/04/27 20:27:39	1.12
@@ -139,8 +139,8 @@
         if self.only:
             _md=md
             md=TemplateDict()
-            if hasattr(_md, 'validate'):
-                md.validate=_md.validate
+            if hasattr(_md, 'read_guard'):
+                md.read_guard = _md.read_guard
 
         md._push(v)
         try: return render_blocks(self.section, md)

--- Updated File DocumentTemplate.py in package Zope2 --
--- DocumentTemplate.py	1999/03/10 00:15:08	1.10
+++ DocumentTemplate.py	2001/04/27 20:27:39	1.11
@@ -145,32 +145,16 @@
 
     Document templates provide a basic level of access control by
     preventing access to names beginning with an underscore.
-    Addational control may be provided by providing document templates
-    with a 'validate' method.  This would typically be done by
+    Additional control may be provided by providing document templates
+    with a 'read_guard' method.  This would typically be done by
     subclassing one or more of the DocumentTemplate classes.
 
-    If provided, the the 'validate' method will be called when objects
+    If provided, the the 'read_guard' method will be called when objects
     are accessed as accessed as instance attributes or when they are
-    accessed through keyed access in an expression..  The 'validate'
-    method will be called with five arguments:
+    accessed through keyed access in an expression..  The 'read_guard'
+    method will be called with the containing object.  It can
+    return a wrapper object from which the attribute will be accessed.
 
-    1. The containing object that the object was accessed from,
-
-    2. The actual containing object that the object was found in,
-       which may be different from the containing onject the object
-       was accessed from, if the containing object supports
-       acquisition,
-
-    3. The name used to acces the object,
-
-    4. The object, and
-
-    5. The namespace object used to render the document template.
-
-       If a document template was called from Bobo, then the namespace
-       object will have an attribute, AUTHENTICATED_USER that is the
-       user object that was found if and when Bobo authenticated a user.
-
 Document Templates may be created 4 ways:
 
     DocumentTemplate.String -- Creates a document templated from a
@@ -201,4 +185,3 @@
 from DT_String import String, File
 from DT_HTML import HTML, HTMLFile, HTMLDefault
 # import DT_UI # Install HTML editing
-from DT_Util import html_quote

--- Updated File __init__.py in package Zope2 --
--- __init__.py	1999/05/17 16:14:59	1.14
+++ __init__.py	2001/04/27 20:27:39	1.15
@@ -92,4 +92,3 @@
 
 import ExtensionClass # work-around for import bug.
 from DocumentTemplate import String, File, HTML, HTMLDefault, HTMLFile
-from DocumentTemplate import html_quote

--- Updated File cDocumentTemplate.c in package Zope2 --
--- cDocumentTemplate.c	2000/11/21 22:08:50	1.35
+++ cDocumentTemplate.c	2001/04/27 20:27:39	1.36
@@ -92,7 +92,7 @@
 static PyObject *py_isDocTemp=0, *py_blocks=0, *py_=0, *join=0, *py_acquire;
 static PyObject *py___call__, *py___roles__, *py_AUTHENTICATED_USER;
 static PyObject *py_hasRole, *py__proxy_roles, *py_Unauthorized;
-static PyObject *py_Unauthorized_fmt, *py_validate;
+static PyObject *py_Unauthorized_fmt, *py_read_guard;
 static PyObject *py__push, *py__pop, *py_aq_base, *py_renderNS;
 
 /* ----------------------------------------------------- */
@@ -108,7 +108,7 @@
   PyObject *inst;
   PyObject *cache;
   PyObject *namespace;
-  PyObject *validate;
+  PyObject *read_guard;
 } InstanceDictobject;
 
 staticforward PyExtensionClass InstanceDictType;
@@ -116,18 +116,18 @@
 static PyObject *
 InstanceDict___init__(InstanceDictobject *self, PyObject *args)
 {
-  self->validate=NULL;
+  self->read_guard=NULL;
   UNLESS(PyArg_ParseTuple(args, "OO|O",
 			  &(self->inst),
 			  &(self->namespace),
-			  &(self->validate)))
+			  &(self->read_guard)))
     return NULL;
   Py_INCREF(self->inst);
   Py_INCREF(self->namespace);
-  if (self->validate)
-    Py_INCREF(self->validate);
+  if (self->read_guard)
+    Py_INCREF(self->read_guard);
   else
-    UNLESS(self->validate=PyObject_GetAttr(self->namespace, py_validate))
+    UNLESS(self->read_guard=PyObject_GetAttr(self->namespace, py_read_guard))
        return NULL;
     
   UNLESS(self->cache=PyDict_New()) return NULL;
@@ -150,7 +150,7 @@
   Py_XDECREF(self->inst);
   Py_XDECREF(self->cache);
   Py_XDECREF(self->namespace);
-  Py_XDECREF(self->validate);
+  Py_XDECREF(self->read_guard);
   Py_DECREF(self->ob_type);
   PyMem_DEL(self);
 }
@@ -182,7 +182,7 @@
   char *name;
   
   /* Try to get value from the cache */
-  if (r=PyObject_GetItem(self->cache, key)) return r;
+  if ((r=PyObject_GetItem(self->cache, key))) return r;
   PyErr_Clear();
   
   /* Check for __str__ */
@@ -193,56 +193,31 @@
       return PyObject_Str(self->inst);
     }
   
-  /* Do explicit acquisition with "roles" rule */
-  if (r=PyObject_GetAttr(self->inst, py_acquire))
-    {
-      /* Sanity check in case of explicit Aq */
-      if (v=PyObject_GetAttr(self->inst, key)) Py_DECREF(v);  
-      else 
-	{
-	  Py_DECREF(r);
-	  goto KeyError;
-	}
-
-      if (self->validate != Py_None)
-	{
-	  UNLESS_ASSIGN(r,PyObject_CallFunction(
-		 r, "OOO", key, self->validate, self->namespace))
-	    {
-	      PyObject *tb;
-
-	      PyErr_Fetch(&r, &v, &tb);
-	      if (r != PyExc_AttributeError || PyObject_Compare(v,key))
-		{
-		  PyErr_Restore(r,v,tb);
-		  return NULL;
-		}
-	      Py_XDECREF(r);
-	      Py_XDECREF(v);
-	      Py_XDECREF(tb);
-	      
-	      goto KeyError;
-	    }
-	}
-      else
-	UNLESS_ASSIGN(r, PyObject_GetAttr(self->inst, key)) goto KeyError;
-    }  
-  else
-    {
-      PyErr_Clear();
+  if (self->read_guard != Py_None) {
+    r = PyObject_CallFunction(self->read_guard, "O", self->inst);
+    if (!r) return NULL;
+  }
+  else {
+    r = self->inst;
+    Py_INCREF(r);
+  }
 
-      /* OK, use getattr */
-      UNLESS(r=PyObject_GetAttr(self->inst, key)) goto KeyError;
+  ASSIGN(r, PyObject_GetAttr(r, key));
+  if (!r) {
+    PyObject *tb;
+
+    PyErr_Fetch(&r, &v, &tb);
+    if (r != PyExc_AttributeError) /* || PyObject_Compare(v,key)) */
+      {
+	PyErr_Restore(r,v,tb);
+	return NULL;
+      }
+    Py_XDECREF(r);
+    Py_XDECREF(v);
+    Py_XDECREF(tb);
 
-      if (self->validate != Py_None)
-	{
-	  UNLESS(v=PyObject_CallFunction(
-	    self->validate,"OOOOO",
-	    self->inst, self->inst, key, r, self->namespace))
-	    return NULL;
-	  Py_DECREF(v);
-	}
-    }
+    goto KeyError;
+  }
   
   if (r && PyObject_SetItem(self->cache, key, r) < 0) PyErr_Clear();
   
@@ -312,9 +287,7 @@
 staticforward PyExtensionClass MMtype;
 
 static PyObject *
-MM_push(self, args)
-	MM *self;
-	PyObject *args;
+MM_push(MM *self, PyObject *args)
 {
   PyObject *src;
   UNLESS(PyArg_Parse(args, "O", &src)) return NULL;
@@ -324,9 +297,7 @@
 }
 
 static PyObject *
-MM_pop(self, args)
-	MM *self;
-	PyObject *args;
+MM_pop(MM *self, PyObject *args)
 {
   int i=1, l;
   PyObject *r;
@@ -343,9 +314,7 @@
 }
 
 static PyObject *
-MM__init__(self, args)
-     MM *self;
-     PyObject *args;
+MM__init__(MM *self, PyObject *args)
 {
   UNLESS(PyArg_Parse(args, "")) return NULL;
   UNLESS(self->data=PyList_New(0)) return NULL;
@@ -385,7 +354,7 @@
     Py_INCREF(base);
   }
 
-  if ( value = PyObject_GetAttr(base, py_isDocTemp) ) {
+  if ( (value = PyObject_GetAttr(base, py_isDocTemp)) ) {
     if (PyObject_IsTrue(value)) {
       result = 1;
     }
@@ -408,12 +377,12 @@
   while (--i >= 0)
     {
       e=PyList_GetItem(self->data,i);
-      if (e=PyObject_GetItem(e,key))
+      if ((e=PyObject_GetItem(e,key)))
 	{
           if (!call) return e;
 
           /* Try calling __render_with_namespace__ */
-          if (rr = PyObject_GetAttr(e, py_renderNS)) 
+          if ((rr = PyObject_GetAttr(e, py_renderNS))) 
             {
               Py_DECREF(e);
               UNLESS_ASSIGN(rr, PyObject_CallFunction(rr, "O", self))
@@ -516,8 +485,7 @@
 };
 
 static void
-MM_dealloc(self)
-     MM *self;
+MM_dealloc(MM *self)
 {
   Py_XDECREF(self->data);
   Py_XDECREF(self->dict);
@@ -538,7 +506,7 @@
     {
       PyObject *v;
 
-      if (v=PyDict_GetItem(self->dict, name))
+      if ((v=PyDict_GetItem(self->dict, name)))
 	{
 	  Py_INCREF(v);
 	  return v;
@@ -568,8 +536,7 @@
 }
 
 static int
-MM_length(self)
-	MM *self;
+MM_length(MM *self)
 {
   long l=0, el, i;
   PyObject *e=0;
@@ -800,7 +767,7 @@
 		    {
 		      /* We have to be careful to handle key errors here */
 		      n=cond;
-		      if (cond=PyObject_GetItem(md,cond))
+		      if ((cond=PyObject_GetItem(md,cond)))
 			{
 			  if (PyDict_SetItem(cache, n, cond) < 0)
 			    {
@@ -912,7 +879,7 @@
 };
 
 void
-initcDocumentTemplate()
+initcDocumentTemplate(void)
 {
   PyObject *m, *d;
   char *rev="$Revision$";
@@ -927,7 +894,7 @@
   UNLESS(py___roles__=PyString_FromString("__roles__")) return;
   UNLESS(py__proxy_roles=PyString_FromString("_proxy_roles")) return;
   UNLESS(py_hasRole=PyString_FromString("hasRole")) return;
-  UNLESS(py_validate=PyString_FromString("validate")) return;
+  UNLESS(py_read_guard=PyString_FromString("read_guard")) return;
   UNLESS(py__push=PyString_FromString("_push")) return;
   UNLESS(py__pop=PyString_FromString("_pop")) return;
   UNLESS(py_aq_base=PyString_FromString("aq_base")) return;

--- Updated File pDocumentTemplate.py in package Zope2 --
--- pDocumentTemplate.py	2001/01/16 18:08:27	1.27
+++ pDocumentTemplate.py	2001/04/27 20:27:39	1.28
@@ -117,14 +117,14 @@
 
 class InstanceDict:
 
-    validate=None
+    read_guard=None
 
-    def __init__(self,o,namespace,validate=None):
+    def __init__(self,o,namespace,read_guard=None):
         self.self=o
         self.cache={}
         self.namespace=namespace
-        if validate is None: self.validate=namespace.validate
-        else: self.validate=validate
+        if read_guard is None: self.read_guard=namespace.read_guard
+        else: self.read_guard=read_guard
 
     def has_key(self,key):
         return hasattr(self.self,key)
@@ -144,14 +144,16 @@
         if key[:1]=='_':
             if key != '__str__':
                 raise KeyError, key # Don't divuldge private data
-            r=str(inst)
-        else:
-            try: r=getattr(inst,key)
-            except AttributeError: raise KeyError, key
+            else:
+                return str(inst)
 
-        v=self.validate
-        if v is not None: v(inst,inst,key,r,self.namespace)
+        read_guard = self.read_guard
+        if read_guard is not None:
+            inst = read_guard(inst)
 
+        try: r = getattr(inst, key)
+        except AttributeError: raise KeyError, key
+
         self.cache[key]=r
         return r
 
@@ -212,9 +214,14 @@
                 return v.__render_with_namespace__(self)
             vbase = getattr(v, 'aq_base', v)
             if callable(vbase):
-                if getattr(vbase, 'isDocTemp', None):
-                    return v(None, self)
-                return v()
+                try:
+                    if getattr(vbase, 'isDocTemp', 0):
+                        v = v(None, self)
+                    else:
+                        v = v()
+                except AttributeError, n:
+                    if n != '__call__':
+                        raise
         return v
 
     def has_key(self,key):