[Zope-Checkins] CVS: Zope2 - RestrictionMutator.py:1.1.2.3
shane@digicool.com
shane@digicool.com
Thu, 19 Apr 2001 18:50:11 -0400 (EDT)
Update of /cvs-repository/Zope2/lib/python/RestrictedPython
In directory korak:/tmp/cvs-serv5343
Modified Files:
Tag: RestrictedPythonBranch
RestrictionMutator.py
Log Message:
Rearranged some more and added a compile() lookalike function.
--- Updated File RestrictionMutator.py in package Zope2 --
--- RestrictionMutator.py 2001/04/18 21:02:18 1.1.2.2
+++ RestrictionMutator.py 2001/04/19 22:50:11 1.1.2.3
@@ -1,9 +1,11 @@
import string
-from compiler import ast
-from compiler.transformer import parse
+import MutatingWalker
+from compiler import ast, visitor, pycodegen
+from compiler.transformer import Transformer, parse
from compiler.consts import OP_ASSIGN, OP_DELETE, OP_APPLY
+
def rmLineno(node):
'''Strip lineno attributes from a code tree'''
if node.__dict__.has_key('lineno'):
@@ -22,13 +24,13 @@
'''Make a "clean" expression node'''
return stmtNode(txt).expr
-_decl_globals_code = ast.Global(['_print_target_class', '_guards'])
-_prep_guards_code = stmtNode('_read_guard, _write_guard = _guards')
+_decl_globals_code = ast.Global(['_print_target_class', '_guard_init'])
+_prep_guards_code = stmtNode('_guard = _guard_init')
_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')
+_read_guard_name = ast.Name('_guard')
+_write_guard_name = ast.Name('_guard')
class PrintCollector:
@@ -40,24 +42,27 @@
def __call__(self):
return string.join(self.txt, '')
-class RestrictionMutator:
+
+class FuncInfo:
_print_used = 0
_printed_used = 0
+
+class RestrictionMutator:
+ funcinfo = None
+
def __init__(self):
- self._print_stack = []
- self.used_names = {}
self.warnings = []
+ self.used_names = {}
def raiseSyntaxError(self, node, info):
lineno = getattr(node, 'lineno', None)
- if lineno is not None:
+ if lineno is not None and lineno > 0:
raise SyntaxError, ('Line %d: %s' % (lineno, info))
else:
raise SyntaxError, info
- def checkName(self, node):
- name = node.name
+ def checkName(self, node, name):
if len(name) > 1 and name[0] == '_':
# Note: "_" *is* allowed.
self.raiseSyntaxError(
@@ -67,9 +72,9 @@
node, '"printed" is a reserved name.')
def checkAttrName(self, node):
- # This prevents access to protected attributes of wrappers
+ # This prevents access to protected attributes of guards
# and is thus essential regardless of the security policy,
- # unless we use cells...
+ # unless some other solution is devised.
name = node.attrname
if len(name) > 1 and name[0] == '_':
# Note: "_" *is* allowed.
@@ -77,45 +82,52 @@
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
+ self.checkName(node, node.name)
+ for argname in node.argnames:
+ self.checkName(node, argname)
+ former_funcinfo = self.funcinfo
+ self.funcinfo = funcinfo = FuncInfo()
node = walker.defaultVisitNode(node)
- if self._print_used or self._printed_used:
+ if funcinfo._print_used or funcinfo._printed_used:
# Add code at top of function for creating _print_target
node.code.nodes.insert(0, _print_code)
- if not self._printed_used:
+ if not funcinfo._printed_used:
self.warnings.append(
"Prints, but never reads 'printed' variable.")
- elif not self._print_used:
+ elif not funcinfo._print_used:
self.warnings.append(
"Doesn't print, but reads 'printed' variable.")
node.code.nodes[0:0] = [_decl_globals_code, _prep_guards_code]
- self._print_used, self._printed_used = self._print_stack.pop()
+ 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)
- if self._print_stack:
+ funcinfo = self.funcinfo
+ if funcinfo is not None:
if node.dest is None:
- self._print_used = 1
+ funcinfo._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:
+ if node.name == 'printed' and self.funcinfo is not None:
# Replace name lookup with an expression.
- self._printed_used = 1
+ self.funcinfo._printed_used = 1
return _printed_expr
- self.checkName(node)
+ self.checkName(node, node.name)
self.used_names[node.name] = 1
return node
def visitAssName(self, node, walker):
- self.checkName(node)
+ self.checkName(node, node.name)
return node
def visitGetattr(self, node, walker):
@@ -143,13 +155,66 @@
return node
def visitExec(self, node, walker):
- raise SyntaxError, 'Exec statements are not allowed.'
+ self.raiseSyntaxError(node, 'Exec statements are not allowed.')
def visitClass(self, node, walker):
- self.checkName(node)
+ # Should classes be allowed at all??
+ self.checkName(node, node.name)
return walker.defaultVisitNode(node)
+DEBUG = 1
+
+def compile_restricted(s, name, kind,
+ used_names=None, warnings=None):
+ '''
+ Returns restricted compiled code.
+ '''
+ if DEBUG:
+ from time import clock
+ start = clock()
+
+ rm = RestrictionMutator()
+ if kind == 'exec':
+ tree = Transformer().parsesuite(s)
+ MutatingWalker.walk(tree, rm)
+ gen = pycodegen.NestedScopeModuleCodeGenerator(name)
+ visitor.walk(tree, gen)
+ code = gen.getCode()
+ elif kind == 'eval':
+ tree = Transformer().parseexpr(s)
+ MutatingWalker.walk(tree, rm)
+ # XXX No "NestedScopeEvalCodeGenerator" exists
+ # so here's a hack that gets around it.
+ gen = pycodegen.NestedScopeModuleCodeGenerator(name)
+ tree = ast.Module(None, ast.Stmt([ast.Assign([ast.AssName(
+ '__', OP_ASSIGN)], ast.Lambda((), (), 0, tree))]))
+ visitor.walk(tree, gen)
+ code = gen.getCode()
+ d = {}
+ exec code in d
+ code = d['__'].func_code
+ else:
+ raise ValueError, 'Unsupported compile kind: ' + kind
+
+ if used_names is not None:
+ # Fill with the names used.
+ used_names.update(rm.used_names)
+ if warnings is not None:
+ # Fill in warnings.
+ warnings.extend(rm.warnings)
+
+ if DEBUG:
+ end = clock()
+ print 'compile_restricted: %d ms for %s' % (
+ (end - start) * 1000, repr(s))
+## import dis
+## dis.dis(code)
+## print `code.co_code`
+ return code
+
+
+
class Noisy:
'''Test guard class that babbles about accesses'''
def __init__(self, _ob):
@@ -187,7 +252,6 @@
print '__setslice__', `_ob`, lo, hi, value
_ob[lo:hi] = value
-
if __name__ == '__main__':
tree = parse('''
def f():
@@ -195,17 +259,13 @@
print "... wOrLd!".lower()
x = {}
x['p'] = printed[1:-1]
- x['p'] += printed * 2
+ x['p'] += (lambda ob: ob * 2)(printed)
return x['p']
''')
- import MutatingWalker
- from compiler.pycodegen import NestedScopeModuleCodeGenerator
- from compiler import visitor
- print tree
MutatingWalker.walk(tree, RestrictionMutator())
print tree
- gen = NestedScopeModuleCodeGenerator('some_python_script')
+ gen = pycodegen.NestedScopeModuleCodeGenerator('some_python_script')
visitor.walk(tree, gen, verbose=1)
code = gen.getCode()
dict = {'__builtins__': None}
@@ -213,7 +273,8 @@
f = dict['f']
f.func_globals.update({
'_print_target_class': PrintCollector,
- '_guards': (Noisy, Noisy),
+ '_guard_init': Noisy,
+ '_guard': Noisy,
})
print f()
#import dis