[Zope-Checkins] CVS: Releases/Zope/lib/python/Products/PageTemplates - PathPrefixes.py:1.1.2.1 Expressions.py:1.43.46.1

Evan Simpson evan@4-am.com
Tue, 29 Jul 2003 15:55:50 -0400


Update of /cvs-repository/Releases/Zope/lib/python/Products/PageTemplates
In directory cvs.zope.org:/tmp/cvs-serv16592

Modified Files:
      Tag: evan-pathprefix-branch
	Expressions.py 
Added Files:
      Tag: evan-pathprefix-branch
	PathPrefixes.py 
Log Message:
Initial implementation of TALES Path prefixes.


=== Added File Releases/Zope/lib/python/Products/PageTemplates/PathPrefixes.py ===
from TALES import _valid_name, CompilerError

_subpath_prefixes = {}

def initialize():
    global guarded_getattr
    from Expressions import guarded_getattr

def registerSubPathPrefix(prefix, compiler=None, handler=None,
                          do_validate=0):
    '''Register a prefix for subpath expressions.

    A prefixed subpath is a subpath of the form "{px}:{arg}",
    where {px} is the name of a prefix, and {arg} is an arbitrary
    (and possibly empty) argument for the prefix.

    When a subpath is prefixed, during compilation the compiler
    (if any) for the prefix is called with {px} and {arg} as
    arguments, and the returned value replaces {arg} in further
    processing.  If no handler is provided, {arg} replaces the
    subpath.

    If a handler is provided, it is called during traversal
    with {prefix}, {arg}, the current traversal object, the list
    of remaining path elements, and the expression context. The value
    returned by the handler replaces the current traversal object.
    If do_validate is true, the security validator is checked.
    '''
    if not _valid_name(prefix):
        raise ValueError, (
            'Invalid subpath prefix "%s"' % prefix)
    if compiler is None and handler is None:
        raise ValueError, ("registerSubPathPrefix requires either "
                           "a compiler or a handler, or both.")
    _subpath_prefixes[str(prefix)] = (compiler, handler, do_validate)

# 'var:x' is replaced with the value of variable 'x'
def var_compiler(prefix, arg):
    arg = arg.strip()
    if not _valid_name(arg):
        raise CompilerError, ('"%s" is not a valid variable name'
                              % arg)
    return arg
def var_handler(prefix, arg, object, path, econtext):
    path.append(econtext.vars[arg])
    return object
registerSubPathPrefix('var', var_compiler, var_handler)

# 'call:' calls the current object.
# 'call:x, y, x' passes variables 'x', 'y', and 'z' as arguments.
def call_compiler(prefix, arg):
    args = [name.strip() for name in arg.split(',')]
    for name in args:
        if not _valid_name(name):
            raise CompilerError, ('"%s" is not a valid variable name'
                                  % name)
    return args
def call_handler(prefix, arg, object, path, econtext):
    args = [econtext.vars[name] for name in arg]
    return object(*args)
registerSubPathPrefix('call',  call_compiler, call_handler)

# 'key:foo' tries to fetch key 'foo' of the current object.
def key_handler(prefix, arg, object, path, econtext):
    return object[arg]
registerSubPathPrefix('key', handler=key_handler, do_validate=1)

# 'item:6' tries to fetch integer key '6' of the current object.
def item_compiler(prefix, arg):
    return int(arg)
registerSubPathPrefix('item', compiler=item_compiler,
                         handler=key_handler, do_validate=1)
# 'attr:foo' tries to fetch attribute 'foo' of the current object.
def attr_compiler(prefix, arg):
    arg = arg.strip()
    if not _valid_name(arg):
        raise CompilerError, ('"%s" is not a valid attribute name'
                              % arg)
    return arg
def attr_handler(prefix, arg, object, path, econtext):
    return guarded_getattr(object, arg)
registerSubPathPrefix('attr', attr_compiler, attr_handler)

# 'fmt:dollars_and_cents' calls standard PythonScript library
# function 'dollars_and_cents' on the current object.

# 'fmt:%.2f' uses the Python formatting operator to format the
# current object as a floating point number with two decimal places.
try:
    from Products.PythonScripts import standard
    _fmt_names = ('whole_dollars', 'dollars_and_cents',
                  'structured_text', 'restructured_text',
                  'sql_quote', 'html_quote', 'url_quote',
                  'url_quote_plus', 'newline_to_br',
                  'thousands_commas', 'url_unquote',
                  'url_unquote_plus', 'urlencode')
except:
    _fmt_names = ()
def fmt_handler(prefix, arg, object, path, econtext):
    if arg in _fmt_names:
        return getattr(standard, arg)(object)
    return arg % object
registerSubPathPrefix('fmt', handler=fmt_handler)


=== Releases/Zope/lib/python/Products/PageTemplates/Expressions.py 1.43 => 1.43.46.1 ===
--- Releases/Zope/lib/python/Products/PageTemplates/Expressions.py:1.43	Thu Sep 26 17:33:17 2002
+++ Releases/Zope/lib/python/Products/PageTemplates/Expressions.py	Tue Jul 29 15:55:43 2003
@@ -23,7 +23,7 @@
 from TALES import Engine, CompilerError, _valid_name, NAME_RE, \
      Undefined, Default, _parse_expr
 from Acquisition import aq_base, aq_inner, aq_parent
-
+import PathPrefixes
 
 _engine = None
 def getEngine():
@@ -32,6 +32,7 @@
         from PathIterator import Iterator
         _engine = Engine(Iterator)
         installHandlers(_engine)
+        PathPrefixes.initialize()
     return _engine
 
 def installHandlers(engine):
@@ -111,6 +112,19 @@
                     raise
     return ob
 
+class SubPathHandler:
+    def __init__(self, prefix, arg, handler, do_validate):
+        self.prefix = prefix
+        self.arg = arg
+        self.handler = handler
+        self.do_validate = do_validate
+    def __call__(self, object, path, econtext, validate):
+        arg = self.arg
+        o = self.handler(self.prefix, arg, object, path, econtext)
+        if self.do_validate and not validate(object, object, arg, o):
+            raise Unauthorized, arg
+        return o
+
 class SubPathExpr:
     def __init__(self, path):
         self._path = path = path.strip().split('/')
@@ -119,10 +133,23 @@
             raise CompilerError, 'Invalid variable name "%s"' % base
         # Parse path
         self._dp = dp = []
+        prefixes = PathPrefixes._subpath_prefixes
         for i in range(len(path)):
             e = path[i]
             if e[:1] == '?' and _valid_name(e[1:]):
                 dp.append((i, e[1:]))
+            elif ':' in e:
+                prefix, arg = e.split(':', 1)
+                if not prefixes.has_key(prefix):
+                    raise CompilerError, (
+                        'Unknown prefix "%s"' % prefix)
+                compiler, handler, do_v = prefixes.get(prefix)
+                if compiler is not None:
+                    arg = compiler(prefix, arg)
+                if handler is None:
+                    path[i] = arg
+                else:
+                    path[i] = SubPathHandler(prefix, arg, handler, do_v)
         dp.reverse()
 
     def _eval(self, econtext,
@@ -147,7 +174,7 @@
         if isinstance(ob, DeferWrapper):
             ob = ob()
         if path:
-            ob = restrictedTraverse(ob, path, getSecurityManager())
+            ob = restrictedTraverse(ob, path, getSecurityManager(), econtext)
         return ob
 
 class PathExpr:
@@ -292,7 +319,7 @@
         return 'defer:%s' % `self._s`
 
 
-def restrictedTraverse(object, path, securityManager,
+def restrictedTraverse(object, path, securityManager, econtext,
                        get=getattr, has=hasattr, N=None, M=[],
                        TupleType=type(()) ):
 
@@ -307,6 +334,12 @@
         if isinstance(name, TupleType):
             object = object(*name)
             continue
+
+        if isinstance(name, SubPathHandler):
+            object = name(object, path, econtext, validate)
+            continue
+
+        name = str(name)
 
         if not name or name[0] == '_':
             # Skip directly to item access