[Zope-Checkins] CVS: Zope/lib/python/RestrictedPython - RCompile.py:1.1.2.1 RCompile_2_1.py:1.1.2.1 SelectCompiler.py:1.1.2.1 MutatingWalker.py:1.4.8.1 RestrictionMutator.py:1.8.8.1 __init__.py:1.3.8.1 Compilers.py:NONE

Shane Hathaway shane@digicool.com
Wed, 19 Dec 2001 15:37:46 -0500


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

Modified Files:
      Tag: RestrictedPython-2_2-branch
	MutatingWalker.py RestrictionMutator.py __init__.py 
Added Files:
      Tag: RestrictedPython-2_2-branch
	RCompile.py RCompile_2_1.py SelectCompiler.py 
Removed Files:
      Tag: RestrictedPython-2_2-branch
	Compilers.py 
Log Message:
Initial work to make RestrictedPython work with either Python 2.1 or
Python 2.2.

Renamed Compilers.py to RCompile_2_1.py and the compiler package to
compiler_2_1.  Added RCompile.py for compiling restricted modules
and SelectCompiler.py for choosing which compiler to use based on whether
the compiler package is in the standard library.


=== Added File Zope/lib/python/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 built-in compiler module.
"""

__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.use_names
    return gen.getCode(), (), gen.rm.warnings, gen.rm.use_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 Zope/lib/python/RestrictedPython/RCompile_2_1.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 sys
from traceback import format_exception_only

def getSyntaxError(source, mode):
    try:
        compile(source, '<string>', mode)
    except SyntaxError:
        err = format_exception_only(SyntaxError, sys.exc_info()[1])
        err = [line.rstrip() for line in err]
    else:
        err = ['Unknown parser error.']
    return None, err, [], {}

from parser import ParserError
from compiler_2_1.transformer import Transformer

def tryParsing(source, mode):
    if mode == 'eval':
        parser = Transformer().parseexpr
    else:
        parser = Transformer().parsesuite
    try:
        return parser(source), None
    except ParserError:
        return None, getSyntaxError(source, mode)

import MutatingWalker
from RestrictionMutator import RestrictionMutator
from compiler_2_1 import ast, visitor, pycodegen

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

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

    new.function(<code>, <globals>)
    '''
    rm = RestrictionMutator()
    # Parse the parameters and body, then combine them.
    tree, err = tryParsing('def f(%s): pass' % p, 'exec')
    if err:
        if len(err) > 1:
            # Drop the first line of the error and adjust the next two.
            err[1].pop(0) 
            err[1][0] = 'parameters: %s\n' % err[1][0][10:-8]
            err[1][1] = '  ' + err[1][1]
        return err
    f = tree.node.nodes[0]
    btree, err = tryParsing(body, 'exec')
    if err: return err
    f.code.nodes = btree.node.nodes
    f.name = 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
    MutatingWalker.walk(tree, rm)
    if rm.errors:
        return None, rm.errors, rm.warnings, rm.used_names
    gen = pycodegen.NestedScopeModuleCodeGenerator(filename)
    visitor.walk(tree, gen)
    return gen.getCode(), (), rm.warnings, rm.used_names

def compile_restricted_exec(s, filename='<string>', nested_scopes=1):
    '''Compile a restricted code suite.'''
    rm = RestrictionMutator()
    tree, err = tryParsing(s, 'exec')
    if err: return err
    MutatingWalker.walk(tree, rm)
    if rm.errors:
        return None, rm.errors, rm.warnings, rm.used_names
    if nested_scopes:
        gen = pycodegen.NestedScopeModuleCodeGenerator(filename)
    else:
        gen = pycodegen.ModuleCodeGenerator(filename)
    visitor.walk(tree, gen)
    return gen.getCode(), (), rm.warnings, rm.used_names

if 1:
  def compile_restricted_eval(s, filename='<string>', nested_scopes=1):
    '''Compile a restricted expression.'''
    r = compile_restricted_exec('def f(): return \\\n' + s, filename,
                                nested_scopes)
    err = r[1]
    if err:
        if len(err) > 1:
            err.pop(0) # Discard first line of error
    else:
        # Extract the code object representing the function body
        r = (r[0].co_consts[1],) + r[1:]
    return r

else:

  def compile_restricted_eval(s, filename='<string>'):
    '''Compile a restricted expression.'''
    rm = RestrictionMutator()
    tree, err = tryParsing(s, 'eval')
    if err:
        err[1].pop(0) # Discard first line of error
        return err
    MutatingWalker.walk(tree, rm)
    if rm.errors:
        return None, rm.errors, rm.warnings, rm.used_names
    # XXX No "EvalCodeGenerator" exists
    # so here's a hack that gets around it.
    gen = pycodegen.ModuleCodeGenerator(filename)
    gen.emit('SET_LINENO', 0)
    visitor.walk(tree, gen)
    gen.emit('RETURN_VALUE')
    return gen.getCode(), (), rm.warnings, rm.used_names

DEBUG = 0
def compile_restricted(source, filename, mode):
    '''Returns restricted compiled code. The signature of this
    function should match the signature of the builtin compile.'''
    if DEBUG:
        from time import clock
        start = clock()

    if mode == 'eval':
        r = compile_restricted_eval(source, filename)
    elif mode == 'exec':
        r = compile_restricted_exec(source, filename)
    else:
        raise ValueError, "compile_restricted() arg 3 must be 'exec' or 'eval'"

    if DEBUG:
        end = clock()
        print 'compile_restricted: %d ms for %s' % (
            (end - start) * 1000, repr(filename))
    code, errors, warnings, used_names = r
    if errors:
        raise SyntaxError, errors[0]
    return code


=== Added File Zope/lib/python/RestrictedPython/SelectCompiler.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
# 
##############################################################################
'''
Compiler selector.
$Id: SelectCompiler.py,v 1.1.2.1 2001/12/19 20:37:15 shane Exp $
'''

try:
    import compiler  # Should only be found if Python >= 2.2.
except ImportError:
    # Use the compiler_2_1 package.
    from compiler_2_1 import ast
    from compiler_2_1.transformer import parse
    from compiler_2_1.consts import OP_ASSIGN, OP_DELETE, OP_APPLY

    from RCompile_2_1 import \
         compile_restricted, \
         compile_restricted_function, \
         compile_restricted_exec, \
         compile_restricted_eval
else:
    # Use the compiler from the standard library.
    from compiler import ast
    from compiler.transformer import parse
    from compiler.consts import OP_ASSIGN, OP_DELETE, OP_APPLY

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




=== Zope/lib/python/RestrictedPython/MutatingWalker.py 1.4 => 1.4.8.1 ===
 __version__='$Revision$'[11:-2]
 
-from compiler import ast
+from SelectCompiler import ast
 
 ListType = type([])
 TupleType = type(())


=== Zope/lib/python/RestrictedPython/RestrictionMutator.py 1.8 => 1.8.8.1 ===
 __version__='$Revision$'[11:-2]
 
-from compiler import ast
-from compiler.transformer import parse
-from compiler.consts import OP_ASSIGN, OP_DELETE, OP_APPLY
+from SelectCompiler import ast, parse, 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


=== Zope/lib/python/RestrictedPython/__init__.py 1.3 => 1.3.8.1 ===
 '''
 
-from Compilers import \
-     compile_restricted, \
-     compile_restricted_function, \
-     compile_restricted_exec, \
-     compile_restricted_eval
-
+from SelectCompiler import *
 from PrintCollector import PrintCollector
 

=== Removed File Zope/lib/python/RestrictedPython/Compilers.py ===