##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (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.14 $'[11:-2]

import exceptions

# This tiny set of safe builtins is extended by users of the module.
# AccessControl.ZopeGuards contains a large set of wrappers for builtins.
# DocumentTemplate.DT_UTil contains a few.

safe_builtins = {}

for name in ['False', 'None', 'True', 'abs', 'basestring', 'bool', 'callable',
             'chr', 'cmp', 'complex', 'divmod', 'float', 'hash',
             'hex', 'id', 'int', 'isinstance', 'issubclass', 'len',
             'long', 'oct', 'ord', 'pow', 'range', 'repr', 'round',
             'str', 'tuple', 'unichr', 'unicode', 'xrange', 'zip']:

    safe_builtins[name] = __builtins__[name]

# Wrappers provided by this module:
# delattr
# setattr

# Wrappers provided by ZopeGuards:
# __import__
# apply
# dict
# enumerate
# filter
# getattr
# hasattr
# iter
# list
# map
# max
# min
# sum
# all
# any

# Builtins that are intentionally disabled
# compile   - don't let them produce new code
# dir       - a general purpose introspector, probably hard to wrap
# execfile  - no direct I/O
# file      - no direct I/O
# globals   - uncontrolled namespace access
# input     - no direct I/O
# locals    - uncontrolled namespace access
# open      - no direct I/O
# raw_input - no direct I/O
# vars      - uncontrolled namespace access

# There are several strings that describe Python.  I think there's no
# point to including these, although they are obviously safe:
# copyright, credits, exit, help, license, quit

# Not provided anywhere.  Do something about these?  Several are
# related to new-style classes, which we are too scared of to support
# <0.3 wink>.  coerce, buffer, and reload are esoteric enough that no
# one should care.

# buffer
# bytes
# bytearray
# classmethod
# coerce
# eval
# intern
# memoryview
# object
# property
# reload
# slice
# staticmethod
# super
# type

for name in dir(exceptions):
    if name[0] != "_":
        safe_builtins[name] = getattr(exceptions, 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 = {dict: True, list: True}.has_key
    Wrapper = _write_wrapper()
    def guard(ob):
        # 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
