[Zope-Checkins] CVS: Zope2 - MutatingWalker.py:1.1.2.1 RestrictionMutator.py:1.1.2.1
shane@digicool.com
shane@digicool.com
Wed, 18 Apr 2001 14:45:43 -0400 (EDT)
Update of /cvs-repository/Zope2/lib/python/RestrictedPython
In directory korak:/tmp/cvs-serv686
Added Files:
Tag: RestrictedPythonBranch
MutatingWalker.py RestrictionMutator.py
Log Message:
Moved from Packages/RestrictedPython.
--- Added File MutatingWalker.py in package Zope2 ---
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):
for name, child in node.__dict__.items():
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 RestrictionMutator.py in package Zope2 ---
import string
from compiler import ast
from compiler.transformer import parse
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
_decl_globals = ast.Global(['_print_target_class', '_read_guard',
'_write_guard'])
_print_code = stmtNode('_print_target = _print_target_class()')
_printed_expr = exprNode('_print_target()')
_print_target_name = ast.Name('_print_target')
_read_guard_name = ast.Name('_read_guard')
_write_guard_name = ast.Name('_write_guard')
class PrintRedirector:
'''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 string.join(self.txt, '')
class RestrictionMutator:
_print_used = 0
_printed_used = 0
def __init__(self):
self._print_stack = []
self.used_names = {}
self.warnings = []
def raiseSyntaxError(self, node, info):
lineno = getattr(node, 'lineno', None)
if lineno is not None:
raise SyntaxError, ('Line %d: %s' % (lineno, info))
else:
raise SyntaxError, info
def checkName(self, node):
name = node.name
if len(name) > 1 and name[0] == '_':
# Note: "_" *is* allowed.
self.raiseSyntaxError(
node, 'Names starting with "_" are not allowed.')
if name == 'printed':
self.raiseSyntaxError(
node, '"printed" is a reserved name.')
def checkAttrName(self, node):
# This prevents access to protected attributes of wrappers
# and is thus essential regardless of the security policy,
# unless we use cells...
name = node.attrname
if len(name) > 1 and name[0] == '_':
# Note: "_" *is* allowed.
self.raiseSyntaxError(
node, 'Attribute names starting with "_" are not allowed.')
def visitFunction(self, node, walker):
self.checkName(node)
self._print_stack.append((self._print_used, self._printed_used))
self._print_used = 0
self._printed_used = 0
node = walker.defaultVisitNode(node)
if self._print_used or self._printed_used:
# Add code at top of function for creating _print_target
node.code.nodes.insert(0, _print_code)
if not self._printed_used:
self.warnings.append(
"Prints, but never reads 'printed' variable.")
elif not self._print_used:
self.warnings.append(
"Doesn't print, but reads 'printed' variable.")
node.code.nodes.insert(0, _decl_globals)
self._print_used, self._printed_used = self._print_stack.pop()
return node
def visitPrint(self, node, walker):
node = walker.defaultVisitNode(node)
if self._print_stack:
if node.dest is None:
self._print_used = 1
node.dest = _print_target_name
return node
visitPrintnl = visitPrint
def visitName(self, node, walker):
if node.name == 'printed' and self._print_stack:
# Replace name lookup with an expression.
self._printed_used = 1
return _printed_expr
self.checkName(node)
self.used_names[node.name] = 1
return node
def visitAssName(self, node, walker):
self.checkName(node)
return node
def visitGetattr(self, node, walker):
self.checkAttrName(node)
node = walker.defaultVisitNode(node)
node.expr = ast.CallFunc(_read_guard_name, [node.expr])
return node
def visitSubscript(self, node, walker):
node = walker.defaultVisitNode(node)
if node.flags == 'OP_APPLY':
node.expr = ast.CallFunc(_read_guard_name, [node.expr])
elif node.flags in ('OP_DELETE', 'OP_ASSIGN'):
node.expr = ast.CallFunc(_write_guard_name, [node.expr])
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])
return node
def visitExec(self, node, walker):
raise SyntaxError, 'Exec statements are not allowed.'
def visitClass(self, node, walker):
self.checkName(node)
return walker.defaultVisitNode(node)
class Noisy:
'''Test class that babbles about accesses'''
def __init__(self, _ob):
self.__dict__['_ob'] = _ob
# Read guard methods
def __len__(self):
_ob = self.__dict__['_ob']
return len(_ob)
def __getattr__(self, name):
_ob = self.__dict__['_ob']
print '__getattr__', `_ob`, name
return getattr(_ob, name)
def __getitem__(self, index):
_ob = self.__dict__['_ob']
print '__getitem__', `_ob`, index
return _ob[index]
def __getslice__(self, lo, hi):
_ob = self.__dict__['_ob']
print '__getslice__', `_ob`, lo, hi
return _ob[lo:hi]
# Write guard methods
def __setattr__(self, name, value):
_ob = self.__dict__['_ob']
print '__setattr__', `_ob`, name, value
setattr(_ob, name, value)
def __setitem__(self, index, value):
_ob = self.__dict__['_ob']
print '__setitem__', `_ob`, index, value
_ob[index] = value
def __setslice__(self, lo, hi, value):
_ob = self.__dict__['_ob']
print '__setslice__', `_ob`, lo, hi, value
_ob[lo:hi] = value
if __name__ == '__main__':
tree = parse('''
def f():
print "Hello",
print "... wOrLd!".lower()
x = {}
x['p'] = printed[1:-1]
x['p'] += printed * 2
return x['p']
''')
import MutatingWalker
from compiler.pycodegen import NestedScopeModuleCodeGenerator
from compiler import visitor
MutatingWalker.walk(tree, RestrictionMutator())
print tree
gen = NestedScopeModuleCodeGenerator('some_python_script')
visitor.walk(tree, gen, verbose=1)
code = gen.getCode()
dict = {'__builtins__': None}
exec code in dict
f = dict['f']
f.func_globals.update({
'_print_target_class': PrintRedirector,
'_read_guard': Noisy,
'_write_guard': Noisy,
})
print f()
#import dis
#dis.dis(f.func_code)
# Test: f can't access f.func_globals (otherwise it can override
# _*_guard)
# Test: no access to builtin getattr. getattr(guarded_ob, '_ob')
# Test: no access to globals()