[Zope-Checkins] CVS: Zope3/lib/python/Zope/RestrictedPython - Eval.py:1.1.2.1 Guards.py:1.1.2.1 MutatingWalker.py:1.1.2.1 PrintCollector.py:1.1.2.1 RCompile.py:1.1.2.1 RestrictionMutator.py:1.1.2.1 Utilities.py:1.1.2.1 ZopeGuards.py:1.1.2.1 __init__.py:1.1.2.1

Fred L. Drake, Jr. fdrake@acm.org
Wed, 27 Feb 2002 17:29:57 -0500


Update of /cvs-repository/Zope3/lib/python/Zope/RestrictedPython
In directory cvs.zope.org:/tmp/cvs-serv17295

Added Files:
      Tag: Zope-3x-branch
	Eval.py Guards.py MutatingWalker.py PrintCollector.py 
	RCompile.py RestrictionMutator.py Utilities.py ZopeGuards.py 
	__init__.py 
Log Message:
Python 2.2 version of RestrictedPython (mostly cleanup).

=== Added File Zope3/lib/python/Zope/RestrictedPython/Eval.py ===
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
# 
##############################################################################
"""Restricted Python Expressions
"""
__rcs_id__='$Id: Eval.py,v 1.1.2.1 2002/02/27 22:29:56 fdrake Exp $'
__version__='$Revision: 1.1.2.1 $'[11:-2]

import string
compile_restricted_eval = None

nltosp = string.maketrans('\r\n','  ')

default_guarded_getattr = getattr # No restrictions.

def default_guarded_getitem(ob, index):
    # No restrictions.
    return ob[index]

PROFILE = 0

class RestrictionCapableEval:
    """A base class for restricted code.
    """
    globals = {'__builtins__': None}
    rcode = None  # restricted
    ucode = None  # unrestricted
    used = None

    def __init__(self, expr):
        """Create a restricted expression

        where:

          expr -- a string containing the expression to be evaluated.
        """
        expr = expr.strip()
        self.__name__ = expr
        expr = string.translate(expr, nltosp)
        self.expr = expr
        self.prepUnrestrictedCode()  # Catch syntax errors.

    def prepRestrictedCode(self):
        if self.rcode is None:
            global compile_restricted_eval
            if compile_restricted_eval is None:
                # Late binding because this will import the whole
                # compiler suite.
                from Zope.RestrictedPython import compile_restricted_eval

            if PROFILE:
                from time import clock
                start = clock()
            co, err, warn, used = compile_restricted_eval(
                self.expr, '<string>')
            if PROFILE:
                end = clock()
                print 'prepRestrictedCode: %d ms for %s' % (
                    (end - start) * 1000, `self.expr`)
            if err:
                raise SyntaxError, err[0]
            self.used = tuple(used.keys())
            self.rcode = co

    def prepUnrestrictedCode(self):
        if self.ucode is None:
            # Use the standard compiler.
            co = compile(self.expr, '<string>', 'eval')
            if self.used is None:
                # Examine the code object, discovering which names
                # the expression needs.
                names=list(co.co_names)
                used={}
                i=0
                code=co.co_code
                l=len(code)
                LOAD_NAME=101   
                HAVE_ARGUMENT=90        
                while(i < l):
                    c=ord(code[i])
                    if c==LOAD_NAME:
                        name=names[ord(code[i+1])+256*ord(code[i+2])]
                        used[name]=1
                        i=i+3
                    elif c >= HAVE_ARGUMENT: i=i+3
                    else: i=i+1
                self.used=tuple(used.keys())
            self.ucode=co

    def eval(self, mapping):
        # This default implementation is probably not very useful. :-(
        # This is meant to be overridden.
        self.prepRestrictedCode()
        code = self.rcode
        d = {'_getattr_': default_guarded_getattr,
             '_getitem_': default_guarded_getitem}
        d.update(self.globals)
        has_key = d.has_key
        for name in self.used:
            try:
                if not has_key(name):
                    d[name] = mapping[name]
            except KeyError:
                # 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):
        return self.eval(kw)



=== Added File Zope3/lib/python/Zope/RestrictedPython/Guards.py ===
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
# 
##############################################################################
from __future__ import nested_scopes

__version__='$Revision: 1.1.2.1 $'[11:-2]


safe_builtins = {}
for name in ('None', 'abs', 'chr', 'divmod', 'float', 'hash', 'hex', 'int',
             'len', 'max', 'min', 'oct', 'ord', 'round', 'str', 'pow',
             'apply', 'callable', 'cmp', 'complex', 'isinstance',
             'issubclass', 'long', 'repr', 'range', 'list', 'tuple',
             'unichr', 'unicode', 'zip', 'map', 'reduce',
             'Exception',
             'ArithmeticError', 'AssertionError', 'AttributeError',
             'EOFError', 'EnvironmentError', 'FloatingPointError',
             'IOError', 'ImportError', 'IndexError', 'KeyError',
             'LookupError', 'NameError', 'OSError', 'OverflowError',
             'RuntimeError', 'StandardError', 'SyntaxError',
             'TypeError', 'ValueError', 'ZeroDivisionError',):
    safe_builtins[name] = __builtins__[name]


def _write_wrapper():
    # Construct the write wrapper class
    def _handler(secattr, error_msg):
        # Make a class method.
        def handler(self, *args):
            try:
                f = getattr(self.ob, secattr)
            except AttributeError:
                raise TypeError, error_msg
            f(*args)
        return handler
    class Wrapper:
        def __len__(self):
            # Required for slices with negative bounds.
            return len(self.ob)
        def __init__(self, ob):
            self.__dict__['ob'] = ob
        __setitem__ = _handler('__guarded_setitem__',
          'object does not support item or slice assignment')
        __delitem__ = _handler('__guarded_delitem__',
          'object does not support item or slice assignment')
        __setattr__ = _handler('__guarded_setattr__',
          'attribute-less object (assign or del)')
        __delattr__ = _handler('__guarded_delattr__',
          'attribute-less object (assign or del)')
    return Wrapper


def _full_write_guard():
    # Nested scope abuse!
    # safetype and Wrapper variables are used by guard()
    safetype = {type([]): 1, type({}): 1}.has_key
    Wrapper = _write_wrapper()
    def guard(ob, safetype=safetype, Wrapper=Wrapper):
        # Don't bother wrapping simple types, or objects that claim to
        # handle their own write security.
        if safetype(type(ob)) or hasattr(ob, '_guarded_writes'):
            return ob
        # Hand the object to the Wrapper instance, then return the instance.
        return Wrapper(ob)
    return guard 
full_write_guard = _full_write_guard()

def guarded_setattr(object, name, value):
    setattr(full_write_guard(object), name, value)
safe_builtins['setattr'] = guarded_setattr

def guarded_delattr(object, name):
    delattr(full_write_guard(object), name)
safe_builtins['delattr'] = guarded_delattr






=== Added File Zope3/lib/python/Zope/RestrictedPython/MutatingWalker.py ===
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
# 
##############################################################################

__version__='$Revision: 1.1.2.1 $'[11:-2]

from compiler import ast

ListType = type([])
TupleType = type(())
SequenceTypes = (ListType, TupleType)

class MutatingWalker:

    def __init__(self, visitor):
        self.visitor = visitor
        self._cache = {}

    def defaultVisitNode(self, node, walker=None, exclude=None):
        for name, child in node.__dict__.items():
            if exclude is not None and name in exclude:
                continue
            v = self.dispatchObject(child)
            if v is not child:
                # Replace the node.
                node.__dict__[name] = v
        return node

    def visitSequence(self, seq):
        res = seq
        for idx in range(len(seq)):
            child = seq[idx]
            v = self.dispatchObject(child)
            if v is not child:
                # Change the sequence.
                if type(res) is ListType:
                    res[idx : idx + 1] = [v]
                else:
                    res = res[:idx] + (v,) + res[idx + 1:]
        return res

    def dispatchObject(self, ob):
        '''
        Expected to return either ob or something that will take
        its place.
        '''
        if isinstance(ob, ast.Node):
            return self.dispatchNode(ob)
        elif type(ob) in SequenceTypes:
            return self.visitSequence(ob)
        else:
            return ob

    def dispatchNode(self, node):
        klass = node.__class__
        meth = self._cache.get(klass, None)
        if meth is None:
            className = klass.__name__
            meth = getattr(self.visitor, 'visit' + className,
                           self.defaultVisitNode)
            self._cache[klass] = meth
        return meth(node, self)

def walk(tree, visitor):
    return MutatingWalker(visitor).dispatchNode(tree)



=== Added File Zope3/lib/python/Zope/RestrictedPython/PrintCollector.py ===
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
# 
##############################################################################

__version__='$Revision: 1.1.2.1 $'[11:-2]

class PrintCollector:
    '''Collect written text, and return it when called.'''
    def __init__(self):
        self.txt = []
    def write(self, text):
        self.txt.append(text)
    def __call__(self):
        return ''.join(self.txt)


=== Added File Zope3/lib/python/Zope/RestrictedPython/RCompile.py ===
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
# 
##############################################################################
"""Compiles restricted code using the compiler module from the
Python standard library.
"""

__version__='$Revision: 1.1.2.1 $'[11:-2]


from compiler import ast, parse, misc, syntax
from compiler.pycodegen import AbstractCompileMode, Expression, \
     Interactive, Module
from traceback import format_exception_only

import MutatingWalker
from RestrictionMutator import RestrictionMutator


def niceParse(source, filename, mode):
    try:
        return parse(source, mode)
    except:
        # Try to make a clean error message using
        # the builtin Python compiler.
        try:
            compile(source, filename, mode)
        except SyntaxError:
            raise
        # Some other error occurred.
        raise


class RestrictedCompileMode (AbstractCompileMode):

    def __init__(self, source, filename):
        self.rm = RestrictionMutator()
        AbstractCompileMode.__init__(self, source, filename)

    def parse(self):
        return niceParse(self.source, self.filename, self.mode)

    def _get_tree(self):
        tree = self.parse()
        rm = self.rm
        MutatingWalker.walk(tree, rm)
        if rm.errors:
            raise SyntaxError, rm.errors[0]
        misc.set_filename(self.filename, tree)
        syntax.check(tree)
        return tree


class RExpression(RestrictedCompileMode, Expression):
    mode = "eval"
    compile = Expression.compile


class RInteractive(RestrictedCompileMode, Interactive):
    mode = "single"
    compile = Interactive.compile


class RModule(RestrictedCompileMode, Module):
    mode = "exec"
    compile = Module.compile


class RFunction(RModule):
    """A restricted Python function built from parts.
    """

    def __init__(self, p, body, name, filename):
        self.params = p
        self.body = body
        self.name = name
        RModule.__init__(self, None, filename)

    def parse(self):
        # Parse the parameters and body, then combine them.
        firstline = 'def f(%s): pass' % self.params
        tree = niceParse(firstline, '<function parameters>', 'exec')
        f = tree.node.nodes[0]
        body_code = niceParse(self.body, self.filename, 'exec')
        # Stitch the body code into the function.
        f.code.nodes = body_code.node.nodes
        f.name = self.name
        # Look for a docstring.
        stmt1 = f.code.nodes[0]
        if (isinstance(stmt1, ast.Discard) and
            isinstance(stmt1.expr, ast.Const) and
            type(stmt1.expr.value) is type('')):
            f.doc = stmt1.expr.value
        return tree


def compileAndTuplize(gen):
    try:
        gen.compile()
    except SyntaxError, v:
        return None, (str(v),), gen.rm.warnings, gen.rm.used_names
    return gen.getCode(), (), gen.rm.warnings, gen.rm.used_names

def compile_restricted_function(p, body, name, filename):
    """Compiles a restricted code object for a function.

    The function can be reconstituted using the 'new' module:

    new.function(<code>, <globals>)
    """
    gen = RFunction(p, body, name, filename)
    return compileAndTuplize(gen)

def compile_restricted_exec(s, filename='<string>'):
    """Compiles a restricted code suite.
    """
    gen = RModule(s, filename)
    return compileAndTuplize(gen)

def compile_restricted_eval(s, filename='<string>'):
    """Compiles a restricted expression.
    """
    gen = RExpression(s, filename)
    return compileAndTuplize(gen)

def compile_restricted(source, filename, mode):
    """Replacement for the builtin compile() function.
    """
    if mode == "single":
        gen = RInteractive(source, filename)
    elif mode == "exec":
        gen = RModule(source, filename)
    elif mode == "eval":
        gen = RExpression(source, filename)
    else:
        raise ValueError("compile_restricted() 3rd arg must be 'exec' or "
                         "'eval' or 'single'")
    gen.compile()
    return gen.getCode()



=== Added File Zope3/lib/python/Zope/RestrictedPython/RestrictionMutator.py ===
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
# 
##############################################################################
'''
RestrictionMutator modifies a tree produced by
compiler.transformer.Transformer, restricting and enhancing the
code in various ways before sending it to pycodegen.
'''
__version__='$Revision: 1.1.2.1 $'[11:-2]

from compiler import ast, parse
from compiler.consts import OP_ASSIGN, OP_DELETE, OP_APPLY

# These utility functions allow us to generate AST subtrees without
# line number attributes.  These trees can then be inserted into other
# trees without affecting line numbers shown in tracebacks, etc.
def rmLineno(node):
    '''Strip lineno attributes from a code tree'''
    if node.__dict__.has_key('lineno'):
        del node.lineno
    for child in node.getChildren():
        if isinstance(child, ast.Node):
            rmLineno(child)

def stmtNode(txt):
    '''Make a "clean" statement node'''
    node = parse(txt).node.nodes[0]
    rmLineno(node)
    return node

def exprNode(txt):
    '''Make a "clean" expression node'''
    return stmtNode(txt).expr

# There should be up to four objects in the global namespace.
# If a wrapper function or print target is needed in a particular
# module or function, it is obtained from one of these objects.
# It is stored in a variable with the same name as the global
# object, but without a single trailing underscore.  This variable is
# local, and therefore efficient to access, in function scopes.
_print_target_name = ast.Name('_print')
_getattr_name = ast.Name('_getattr')
_getattr_name_expr = ast.Name('_getattr_')
_getitem_name = ast.Name('_getitem')
_getitem_name_expr = ast.Name('_getitem_')
_write_guard_name = ast.Name('_write')

# Constants.
_None_const = ast.Const(None)
_write_const = ast.Const('write')

# Example prep code:
#
# global _getattr_
# _getattr = _getattr_
_prep_code = {}
for _n in ('getattr', 'getitem', 'write', 'print'):
    _prep_code[_n] = [ast.Global(['_%s_' % _n]),
                      stmtNode('_%s = _%s_' % (_n, _n))]
# Call the global _print instead of copying it.
_prep_code['print'][1] = stmtNode('_print = _print_()')

_printed_expr = exprNode('_print()')


# Keep track of which restrictions have been applied in a given scope.
class FuncInfo:
    _print_used = 0
    _printed_used = 0
    _getattr_used = 0
    _getitem_used = 0
    _write_used = 0
    _is_suite = 0  # True for modules and functions, false for expressions


class RestrictionMutator:

    def __init__(self):
        self.funcinfo = FuncInfo()
        self.warnings = []
        self.errors = []
        self.used_names = {}

    def error(self, node, info):
        lineno = getattr(node, 'lineno', None)
        if lineno is not None and lineno > 0:
            self.errors.append('Line %d: %s' % (lineno, info))
        else:
            self.errors.append(info)

    def checkName(self, node, name):
        if len(name) > 1 and name[0] == '_':
            # Note: "_" *is* allowed.
            self.error(node, '"%s" is an invalid variable name because'
                       ' it starts with "_"' % name)
        if name == 'printed':
            self.error(node, '"printed" is a reserved name.')

    def checkAttrName(self, node):
        # This prevents access to protected attributes of guards
        # and is thus essential regardless of the security policy,
        # unless some other solution is devised.
        name = node.attrname
        if len(name) > 1 and name[0] == '_':
            # Note: "_" *is* allowed.
            self.error(node, '"%s" is an invalid attribute name '
                       'because it starts with "_".' % name)

    def prepBody(self, body):
        """Appends prep code to the beginning of a code suite.
        """
        info = self.funcinfo
        if info._print_used or info._printed_used:
            # Add code at top for creating _print_target
            body[0:0] = _prep_code['print']
            if not info._printed_used:
                self.warnings.append(
                    "Prints, but never reads 'printed' variable.")
            elif not info._print_used:
                self.warnings.append(
                    "Doesn't print, but reads 'printed' variable.")
        if info._getattr_used:
            body[0:0] = _prep_code['getattr']
        if info._getitem_used:
            body[0:0] = _prep_code['getitem']
        if info._write_used:
            body[0:0] = _prep_code['write']

    def visitFunction(self, node, walker):
        self.checkName(node, node.name)
        for argname in node.argnames:
            self.checkName(node, argname)
        walker.visitSequence(node.defaults)

        former_funcinfo = self.funcinfo
        self.funcinfo = FuncInfo()
        self.funcinfo._is_suite = 1
        node = walker.defaultVisitNode(node, exclude=('defaults',))
        self.prepBody(node.code.nodes)
        self.funcinfo = former_funcinfo
        return node

    def visitLambda(self, node, walker):
        for argname in node.argnames:
            self.checkName(node, argname)
        return walker.defaultVisitNode(node)

    def visitPrint(self, node, walker):
        node = walker.defaultVisitNode(node)
        self.funcinfo._print_used = 1
        if node.dest is None:
            node.dest = _print_target_name
        else:
            self.funcinfo._getattr_used = 1
            # Pre-validate access to the "write" attribute.
            # "print >> ob, x" becomes
            # "print >> (_getattr(ob, 'write') and ob), x"
            node.dest = ast.And([
                ast.CallFunc(_getattr_name, [node.dest, _write_const]),
                node.dest])
        return node

    visitPrintnl = visitPrint

    def visitName(self, node, walker):
        if node.name == 'printed':
            # Replace name lookup with an expression.
            self.funcinfo._printed_used = 1
            return _printed_expr
        self.checkName(node, node.name)
        self.used_names[node.name] = 1
        return node

    def visitAssName(self, node, walker):
        self.checkName(node, node.name)
        return node

    def visitGetattr(self, node, walker):
        self.checkAttrName(node)
        node = walker.defaultVisitNode(node)
        if getattr(node, 'in_aug_assign', 0):
            # We're in an augmented assignment
            # We might support this later...
            self.error(node, 'Augmented assignment of '
                       'attributes is not allowed.')
            #expr.append(_write_guard_name)
            #self.funcinfo._write_used = 1
        self.funcinfo._getattr_used = 1
        if self.funcinfo._is_suite:
            ga = _getattr_name
        else:
            ga = _getattr_name_expr
        return ast.CallFunc(ga, [node.expr, ast.Const(node.attrname)])

    def visitSubscript(self, node, walker):
        node = walker.defaultVisitNode(node)
        if node.flags == OP_APPLY:
            # get subscript or slice
            if getattr(node, 'in_aug_assign', 0):
                # We're in an augmented assignment
                # We might support this later...
                self.error(node, 'Augmented assignment of '
                           'object items and slices is not allowed.')
                #expr.append(_write_guard_name)
                #self.funcinfo._write_used = 1
            self.funcinfo._getitem_used = 1
            if hasattr(node, 'subs'):
                # Subscript.
                subs = node.subs
                if len(subs) > 1:
                    # example: ob[1,2]
                    subs = ast.Tuple(subs)
                else:
                    # example: ob[1]
                    subs = subs[0]
            else:
                # Slice.
                # example: obj[0:2]
                lower = node.lower
                if lower is None:
                    lower = _None_const
                upper = node.upper
                if upper is None:
                    upper = _None_const
                subs = ast.Sliceobj([lower, upper])
            if self.funcinfo._is_suite:
                gi = _getitem_name
            else:
                gi = _getitem_name_expr
            return ast.CallFunc(gi, [node.expr, subs])
        elif node.flags in (OP_DELETE, OP_ASSIGN):
            # set or remove subscript or slice
            node.expr = ast.CallFunc(_write_guard_name, [node.expr])
            self.funcinfo._write_used = 1
        return node

    visitSlice = visitSubscript

    def visitAssAttr(self, node, walker):
        self.checkAttrName(node)
        node = walker.defaultVisitNode(node)
        node.expr = ast.CallFunc(_write_guard_name, [node.expr])
        self.funcinfo._write_used = 1
        return node

    def visitExec(self, node, walker):
        self.error(node, 'Exec statements are not allowed.')

    def visitYield(self, node, walker):
        self.error(node, 'Yield statements are not allowed.')

    def visitClass(self, node, walker):
        # Should classes be allowed at all??
        self.checkName(node, node.name)
        return walker.defaultVisitNode(node)

    def visitModule(self, node, walker):
        self.funcinfo._is_suite = 1
        node = walker.defaultVisitNode(node)
        self.prepBody(node.node.nodes)
        return node

    def visitAugAssign(self, node, walker):
        node.node.in_aug_assign = 1
        return walker.defaultVisitNode(node)



=== Added File Zope3/lib/python/Zope/RestrictedPython/Utilities.py ===
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
# 
##############################################################################

__version__='$Revision: 1.1.2.1 $'[11:-2]

import string, math, random, whrandom

utility_builtins = {}

utility_builtins['string'] = string
utility_builtins['math'] = math
utility_builtins['random'] = random
utility_builtins['whrandom'] = whrandom

try:
    import DateTime
    utility_builtins['DateTime']= DateTime.DateTime
except: pass

def same_type(arg1, *args):
    '''Compares the class or type of two or more objects.'''
    t = getattr(arg1, '__class__', type(arg1))
    for arg in args:
        if getattr(arg, '__class__', type(arg)) is not t:
            return 0
    return 1
utility_builtins['same_type'] = same_type

def test(*args):
    l=len(args)
    for i in range(1, l, 2):
        if args[i-1]: return args[i]

    if l%2: return args[-1]
utility_builtins['test'] = test

def reorder(s, with=None, without=()):
    # s, with, and without are sequences treated as sets.
    # The result is subtract(intersect(s, with), without),
    # unless with is None, in which case it is subtract(s, 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
utility_builtins['reorder'] = reorder


=== Added File Zope3/lib/python/Zope/RestrictedPython/ZopeGuards.py ===
#############################################################################
# 
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
# 
##############################################################################

__version__='$Revision: 1.1.2.1 $'[11:-2]

from Zope.Exceptions import Unauthorized

from Zope.RestrictedPython.Guards import safe_builtins
from Zope.RestrictedPython.Utilities import utility_builtins

from Zope.App.Security.SecurityManagement import getSecurityManager

StringType = type('')

Containers = {list: 1, tuple: 1, dict: 1, str: 1, unicode: 1}.has_key

_marker = object()  # Create a new marker object.

safe_builtins = safe_builtins.copy()
safe_builtins.update(utility_builtins)


def guarded_getattr(inst, name, default=_marker):
    # Try to get the attribute normally so that unusual
    # exceptions are caught early.
    try: v = getattr(inst, name)
    except AttributeError:
        if default is not _marker:
            return default
        raise
    if Containers(type(inst)):
        # Simple type.  Short circuit.
        return v
    getSecurityManager().validate(name, v)
    return v
safe_builtins['getattr'] = guarded_getattr


def guarded_hasattr(object, name):
    try:
        guarded_getattr(object, name)
    except (AttributeError, Unauthorized):
        return 0
    return 1
safe_builtins['hasattr'] = guarded_hasattr


SliceType = type(slice(0))
def guarded_getitem(object, index_or_key):
    if isinstance(index_or_key, SliceType):
        step = index_or_key.step
        if step is not None and step != 1:
            v = object[index_or_key]
        else:
            start = index_or_key.start
            stop = index_or_key.stop
            if start is None:
                start = 0
            if stop is None:
                v = object[start:]
            else:
                v = object[start:stop]
        # We don't guard slices.
        return v
    v = object[index_or_key]
    if Containers(type(object)) and Containers(type(v)):
        # Simple type.  Short circuit.
        return v
    getSecurityManager().validate("#%s" % index_or_key, v)
    return v


def guarded_filter(f, seq, skip_unauthorized=0):
    if isinstance(seq, StringType):
        return filter(f, seq)
    if f is None:
        def f(x): return x
    validate = getSecurityManager().validate
    result = []
    append = result.append
    for i in range(len(seq)):
        el = seq[i]
        try:
            validate("#%d" % i, el)
        except Unauthorized:
            if skip_unauthorized:
                pass
            else:
                raise
        else:
            if f(el):
                append(el)
    return result
safe_builtins['filter'] = guarded_filter


## from SecurityInfo import secureModule

## import sys
## def guarded_import(mname, globals={}, locals={}, fromlist=None):
##     mnameparts = mname.split('.')
##     firstmname = mnameparts[0]
##     validate = getSecurityManager().validate
##     module = load_module(None, None, mnameparts, validate, globals, locals)
##     if module is not None:
##         if fromlist is None:
##             fromlist = ()
##         try:
##             for name in fromlist:
##                 if name == '*':
##                     raise ImportError, ('"from %s import *" is not allowed'
##                                         % mname)
##                 v = getattr(module, name, None)
##                 if v is None:
##                     v = load_module(module, mname, [name], validate,
##                                     globals, locals)
##                 validate(name, v)
##                 raise Unauthorized
##             else:
##                 return __import__(mname, globals, locals, fromlist)
##         except Unauthorized:
##             raise ImportError, ('import of "%s" from "%s" is unauthorized'
##                                 % (name, mname))
##     raise ImportError, 'import of "%s" is unauthorized' % mname
## safe_builtins['__import__'] = guarded_import

## def load_module(module, mname, mnameparts, validate, globals, locals):
##     modules = sys.modules
##     while mnameparts:
##         nextname = mnameparts.pop(0)
##         if mname is None:
##             mname = nextname
##         else:
##             mname = '%s.%s' % (mname, nextname)
##         nextmodule = modules.get(mname)
##         if nextmodule is None:
##             nextmodule = secureModule(mname, globals, locals)
##             if nextmodule is None:
##                 return
##         else:
##             secureModule(mname)
##         if module is not None:
##             # At the outer layer ("import foo"), there is no context
##             # against which to validate.
##             validate(nextname, nextmodule)
##         module = nextmodule
##     return module


=== Added File Zope3/lib/python/Zope/RestrictedPython/__init__.py ===
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
# 
##############################################################################
'''
RestrictedPython package.
$Id: __init__.py,v 1.1.2.1 2002/02/27 22:29:56 fdrake Exp $
'''

from RCompile import \
     compile_restricted, \
     compile_restricted_function, \
     compile_restricted_exec, \
     compile_restricted_eval

#from PrintCollector import PrintCollector