Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
R
RestrictedPython-3.6.0
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Boxiang Sun
RestrictedPython-3.6.0
Commits
2ebcf9c5
Commit
2ebcf9c5
authored
Oct 06, 2016
by
Boxiang Sun
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Let RestrictedPython become stubs.
parent
1e456686
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
264 additions
and
261 deletions
+264
-261
src/RestrictedPython/MutatingWalker.py
src/RestrictedPython/MutatingWalker.py
+54
-54
src/RestrictedPython/RCompile.py
src/RestrictedPython/RCompile.py
+206
-203
src/RestrictedPython/SelectCompiler.py
src/RestrictedPython/SelectCompiler.py
+4
-4
No files found.
src/RestrictedPython/MutatingWalker.py
View file @
2ebcf9c5
...
@@ -13,62 +13,62 @@
...
@@ -13,62 +13,62 @@
__version__
=
'$Revision: 1.6 $'
[
11
:
-
2
]
__version__
=
'$Revision: 1.6 $'
[
11
:
-
2
]
from
SelectCompiler
import
ast
#
from SelectCompiler import ast
ListType
=
type
([])
ListType
=
type
([])
TupleType
=
type
(())
TupleType
=
type
(())
SequenceTypes
=
(
ListType
,
TupleType
)
SequenceTypes
=
(
ListType
,
TupleType
)
class
MutatingWalker
:
#
class MutatingWalker:
#
def
__init__
(
self
,
visitor
):
#
def __init__(self, visitor):
self
.
visitor
=
visitor
#
self.visitor = visitor
self
.
_cache
=
{}
#
self._cache = {}
#
def
defaultVisitNode
(
self
,
node
,
walker
=
None
,
exclude
=
None
):
#
def defaultVisitNode(self, node, walker=None, exclude=None):
for
name
,
child
in
node
.
__dict__
.
items
():
#
for name, child in node.__dict__.items():
if
exclude
is
not
None
and
name
in
exclude
:
#
if exclude is not None and name in exclude:
continue
#
continue
v
=
self
.
dispatchObject
(
child
)
#
v = self.dispatchObject(child)
if
v
is
not
child
:
#
if v is not child:
# Replace the node.
#
# Replace the node.
node
.
__dict__
[
name
]
=
v
#
node.__dict__[name] = v
return
node
#
return node
#
def
visitSequence
(
self
,
seq
):
#
def visitSequence(self, seq):
res
=
seq
#
res = seq
for
idx
in
range
(
len
(
seq
)):
#
for idx in range(len(seq)):
child
=
seq
[
idx
]
#
child = seq[idx]
v
=
self
.
dispatchObject
(
child
)
#
v = self.dispatchObject(child)
if
v
is
not
child
:
#
if v is not child:
# Change the sequence.
#
# Change the sequence.
if
type
(
res
)
is
ListType
:
#
if type(res) is ListType:
res
[
idx
:
idx
+
1
]
=
[
v
]
#
res[idx : idx + 1] = [v]
else
:
#
else:
res
=
res
[:
idx
]
+
(
v
,)
+
res
[
idx
+
1
:]
#
res = res[:idx] + (v,) + res[idx + 1:]
return
res
#
return res
#
def
dispatchObject
(
self
,
ob
):
#
def dispatchObject(self, ob):
'''
#
'''
Expected to return either ob or something that will take
#
Expected to return either ob or something that will take
its place.
#
its place.
'''
#
'''
if
isinstance
(
ob
,
ast
.
Node
):
#
if isinstance(ob, ast.Node):
return
self
.
dispatchNode
(
ob
)
#
return self.dispatchNode(ob)
elif
type
(
ob
)
in
SequenceTypes
:
#
elif type(ob) in SequenceTypes:
return
self
.
visitSequence
(
ob
)
#
return self.visitSequence(ob)
else
:
#
else:
return
ob
#
return ob
#
def
dispatchNode
(
self
,
node
):
#
def dispatchNode(self, node):
klass
=
node
.
__class__
#
klass = node.__class__
meth
=
self
.
_cache
.
get
(
klass
,
None
)
#
meth = self._cache.get(klass, None)
if
meth
is
None
:
#
if meth is None:
className
=
klass
.
__name__
#
className = klass.__name__
meth
=
getattr
(
self
.
visitor
,
'visit'
+
className
,
#
meth = getattr(self.visitor, 'visit' + className,
self
.
defaultVisitNode
)
#
self.defaultVisitNode)
self
.
_cache
[
klass
]
=
meth
#
self._cache[klass] = meth
return
meth
(
node
,
self
)
#
return meth(node, self)
#
def
walk
(
tree
,
visitor
):
#
def walk(tree, visitor):
return
MutatingWalker
(
visitor
).
dispatchNode
(
tree
)
#
return MutatingWalker(visitor).dispatchNode(tree)
src/RestrictedPython/RCompile.py
View file @
2ebcf9c5
...
@@ -16,65 +16,65 @@ Python standard library.
...
@@ -16,65 +16,65 @@ Python standard library.
__version__
=
'$Revision: 1.6 $'
[
11
:
-
2
]
__version__
=
'$Revision: 1.6 $'
[
11
:
-
2
]
from
compiler
import
ast
,
parse
,
misc
,
syntax
,
pycodegen
#
from compiler import ast, parse, misc, syntax, pycodegen
from
compiler.pycodegen
import
AbstractCompileMode
,
Expression
,
\
#
from compiler.pycodegen import AbstractCompileMode, Expression, \
Interactive
,
Module
,
ModuleCodeGenerator
,
FunctionCodeGenerator
,
findOp
#
Interactive, Module, ModuleCodeGenerator, FunctionCodeGenerator, findOp
#
import
MutatingWalker
#
import MutatingWalker
from
RestrictionMutator
import
RestrictionMutator
#
from RestrictionMutator import RestrictionMutator
#
#
def
niceParse
(
source
,
filename
,
mode
):
#
def niceParse(source, filename, mode):
if
isinstance
(
source
,
unicode
):
#
if isinstance(source, unicode):
# Use the utf-8-sig BOM so the compiler
#
# Use the utf-8-sig BOM so the compiler
# detects this as a UTF-8 encoded string.
#
# detects this as a UTF-8 encoded string.
source
=
'
\
xef
\
xbb
\
xbf
'
+
source
.
encode
(
'utf-8'
)
#
source = '\xef\xbb\xbf' + source.encode('utf-8')
try
:
#
try:
return
parse
(
source
,
mode
)
#
return parse(source, mode)
except
:
#
except:
# Try to make a clean error message using
#
# Try to make a clean error message using
# the builtin Python compiler.
#
# the builtin Python compiler.
try
:
#
try:
compile
(
source
,
filename
,
mode
)
#
compile(source, filename, mode)
except
SyntaxError
:
#
except SyntaxError:
raise
#
raise
# Some other error occurred.
#
# Some other error occurred.
raise
#
raise
#
class
RestrictedCompileMode
(
AbstractCompileMode
):
#
class RestrictedCompileMode(AbstractCompileMode):
"""Abstract base class for hooking up custom CodeGenerator."""
#
"""Abstract base class for hooking up custom CodeGenerator."""
# See concrete subclasses below.
#
# See concrete subclasses below.
#
def
__init__
(
self
,
source
,
filename
):
#
def __init__(self, source, filename):
if
source
:
#
if source:
source
=
'
\
n
'
.
join
(
source
.
splitlines
())
+
'
\
n
'
#
source = '\n'.join(source.splitlines()) + '\n'
self
.
rm
=
RestrictionMutator
()
#
self.rm = RestrictionMutator()
AbstractCompileMode
.
__init__
(
self
,
source
,
filename
)
#
AbstractCompileMode.__init__(self, source, filename)
#
def
parse
(
self
):
#
def parse(self):
return
niceParse
(
self
.
source
,
self
.
filename
,
self
.
mode
)
#
return niceParse(self.source, self.filename, self.mode)
#
def
_get_tree
(
self
):
#
def _get_tree(self):
tree
=
self
.
parse
()
#
tree = self.parse()
MutatingWalker
.
walk
(
tree
,
self
.
rm
)
#
MutatingWalker.walk(tree, self.rm)
if
self
.
rm
.
errors
:
#
if self.rm.errors:
raise
SyntaxError
,
self
.
rm
.
errors
[
0
]
#
raise SyntaxError, self.rm.errors[0]
misc
.
set_filename
(
self
.
filename
,
tree
)
#
misc.set_filename(self.filename, tree)
syntax
.
check
(
tree
)
#
syntax.check(tree)
return
tree
#
return tree
#
def
compile
(
self
):
#
def compile(self):
tree
=
self
.
_get_tree
()
#
tree = self._get_tree()
gen
=
self
.
CodeGeneratorClass
(
tree
)
#
gen = self.CodeGeneratorClass(tree)
self
.
code
=
gen
.
getCode
()
#
self.code = gen.getCode()
#
#
def
compileAndTuplize
(
gen
):
#
def compileAndTuplize(gen):
try
:
#
try:
gen
.
compile
()
#
gen.compile()
except
SyntaxError
,
v
:
#
except SyntaxError, v:
return
None
,
(
str
(
v
),),
gen
.
rm
.
warnings
,
gen
.
rm
.
used_names
#
return None, (str(v),), gen.rm.warnings, gen.rm.used_names
return
gen
.
getCode
(),
(),
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
,
globalize
=
None
):
def
compile_restricted_function
(
p
,
body
,
name
,
filename
,
globalize
=
None
):
"""Compiles a restricted code object for a function.
"""Compiles a restricted code object for a function.
...
@@ -87,13 +87,15 @@ def compile_restricted_function(p, body, name, filename, globalize=None):
...
@@ -87,13 +87,15 @@ def compile_restricted_function(p, body, name, filename, globalize=None):
treated as globals (code is generated as if each name in the list
treated as globals (code is generated as if each name in the list
appeared in a global statement at the top of the function).
appeared in a global statement at the top of the function).
"""
"""
gen
=
RFunction
(
p
,
body
,
name
,
filename
,
globalize
)
# gen = RFunction(p, body, name, filename, globalize)
return
compileAndTuplize
(
gen
)
# return compileAndTuplize(gen)
return
None
def
compile_restricted_exec
(
s
,
filename
=
'<string>'
):
def
compile_restricted_exec
(
s
,
filename
=
'<string>'
):
"""Compiles a restricted code suite."""
"""Compiles a restricted code suite."""
gen
=
RModule
(
s
,
filename
)
# gen = RModule(s, filename)
return
compileAndTuplize
(
gen
)
# return compileAndTuplize(gen)
return
None
def
compile_restricted_eval
(
s
,
filename
=
'<string>'
):
def
compile_restricted_eval
(
s
,
filename
=
'<string>'
):
"""Compiles a restricted expression."""
"""Compiles a restricted expression."""
...
@@ -102,143 +104,144 @@ def compile_restricted_eval(s, filename='<string>'):
...
@@ -102,143 +104,144 @@ def compile_restricted_eval(s, filename='<string>'):
def
compile_restricted
(
source
,
filename
,
mode
):
def
compile_restricted
(
source
,
filename
,
mode
):
"""Replacement for the builtin compile() function."""
"""Replacement for the builtin compile() function."""
if
mode
==
"single"
:
# if mode == "single":
gen
=
RInteractive
(
source
,
filename
)
# gen = RInteractive(source, filename)
elif
mode
==
"exec"
:
# elif mode == "exec":
gen
=
RModule
(
source
,
filename
)
# gen = RModule(source, filename)
elif
mode
==
"eval"
:
# elif mode == "eval":
gen
=
RExpression
(
source
,
filename
)
# gen = RExpression(source, filename)
else
:
# else:
raise
ValueError
(
"compile_restricted() 3rd arg must be 'exec' or "
# raise ValueError("compile_restricted() 3rd arg must be 'exec' or "
"'eval' or 'single'"
)
# "'eval' or 'single'")
gen
.
compile
()
# gen.compile()
return
gen
.
getCode
()
# return gen.getCode()
return
None
class
RestrictedCodeGenerator
:
"""Mixin for CodeGenerator to replace UNPACK_SEQUENCE bytecodes.
# class RestrictedCodeGenerator:
# """Mixin for CodeGenerator to replace UNPACK_SEQUENCE bytecodes.
The UNPACK_SEQUENCE opcode is not safe because it extracts
#
elements from a sequence without using a safe iterator or
# The UNPACK_SEQUENCE opcode is not safe because it extracts
making __getitem__ checks.
# elements from a sequence without using a safe iterator or
# making __getitem__ checks.
This code generator replaces use of UNPACK_SEQUENCE with calls to
#
a function that unpacks the sequence, performes the appropriate
# This code generator replaces use of UNPACK_SEQUENCE with calls to
security checks, and returns a simple list.
# a function that unpacks the sequence, performes the appropriate
"""
# security checks, and returns a simple list.
# """
# Replace the standard code generator for assignments to tuples
#
# and lists.
# # Replace the standard code generator for assignments to tuples
# # and lists.
def
_gen_safe_unpack_sequence
(
self
,
num
):
#
# We're at a place where UNPACK_SEQUENCE should be generated, to
# def _gen_safe_unpack_sequence(self, num):
# unpack num items. That's a security hole, since it exposes
# # We're at a place where UNPACK_SEQUENCE should be generated, to
# individual items from an arbitrary iterable. We don't remove
# # unpack num items. That's a security hole, since it exposes
# the UNPACK_SEQUENCE, but instead insert a call to our _getiter_()
# # individual items from an arbitrary iterable. We don't remove
# wrapper first. That applies security checks to each item as
# # the UNPACK_SEQUENCE, but instead insert a call to our _getiter_()
# it's delivered. codegen is (just) a bit messy because the
# # wrapper first. That applies security checks to each item as
# iterable is already on the stack, so we have to do a stack swap
# # it's delivered. codegen is (just) a bit messy because the
# to get things in the right order.
# # iterable is already on the stack, so we have to do a stack swap
self
.
emit
(
'LOAD_GLOBAL'
,
'_getiter_'
)
# # to get things in the right order.
self
.
emit
(
'ROT_TWO'
)
# self.emit('LOAD_GLOBAL', '_getiter_')
self
.
emit
(
'CALL_FUNCTION'
,
1
)
# self.emit('ROT_TWO')
self
.
emit
(
'UNPACK_SEQUENCE'
,
num
)
# self.emit('CALL_FUNCTION', 1)
# self.emit('UNPACK_SEQUENCE', num)
def
_visitAssSequence
(
self
,
node
):
#
if
findOp
(
node
)
!=
'OP_DELETE'
:
# def _visitAssSequence(self, node):
self
.
_gen_safe_unpack_sequence
(
len
(
node
.
nodes
))
# if findOp(node) != 'OP_DELETE':
for
child
in
node
.
nodes
:
# self._gen_safe_unpack_sequence(len(node.nodes))
self
.
visit
(
child
)
# for child in node.nodes:
# self.visit(child)
visitAssTuple
=
_visitAssSequence
#
visitAssList
=
_visitAssSequence
# visitAssTuple = _visitAssSequence
# visitAssList = _visitAssSequence
# Call to generate code for unpacking nested tuple arguments
#
# in function calls.
# # Call to generate code for unpacking nested tuple arguments
# # in function calls.
def
unpackSequence
(
self
,
tup
):
#
self
.
_gen_safe_unpack_sequence
(
len
(
tup
))
# def unpackSequence(self, tup):
for
elt
in
tup
:
# self._gen_safe_unpack_sequence(len(tup))
if
isinstance
(
elt
,
tuple
):
# for elt in tup:
self
.
unpackSequence
(
elt
)
# if isinstance(elt, tuple):
else
:
# self.unpackSequence(elt)
self
.
_nameOp
(
'STORE'
,
elt
)
# else:
# self._nameOp('STORE', elt)
# A collection of code generators that adds the restricted mixin to
#
# handle unpacking for all the different compilation modes. They
# # A collection of code generators that adds the restricted mixin to
# are defined here (at the end) so that can refer to RestrictedCodeGenerator.
# # handle unpacking for all the different compilation modes. They
# # are defined here (at the end) so that can refer to RestrictedCodeGenerator.
class
RestrictedFunctionCodeGenerator
(
RestrictedCodeGenerator
,
#
pycodegen
.
FunctionCodeGenerator
):
# class RestrictedFunctionCodeGenerator(RestrictedCodeGenerator,
pass
# pycodegen.FunctionCodeGenerator):
# pass
class
RestrictedExpressionCodeGenerator
(
RestrictedCodeGenerator
,
#
pycodegen
.
ExpressionCodeGenerator
):
# class RestrictedExpressionCodeGenerator(RestrictedCodeGenerator,
pass
# pycodegen.ExpressionCodeGenerator):
# pass
class
RestrictedInteractiveCodeGenerator
(
RestrictedCodeGenerator
,
#
pycodegen
.
InteractiveCodeGenerator
):
# class RestrictedInteractiveCodeGenerator(RestrictedCodeGenerator,
pass
# pycodegen.InteractiveCodeGenerator):
# pass
class
RestrictedModuleCodeGenerator
(
RestrictedCodeGenerator
,
#
pycodegen
.
ModuleCodeGenerator
):
# class RestrictedModuleCodeGenerator(RestrictedCodeGenerator,
# pycodegen.ModuleCodeGenerator):
def
initClass
(
self
):
#
ModuleCodeGenerator
.
initClass
(
self
)
# def initClass(self):
self
.
__class__
.
FunctionGen
=
RestrictedFunctionCodeGenerator
# ModuleCodeGenerator.initClass(self)
# self.__class__.FunctionGen = RestrictedFunctionCodeGenerator
#
# These subclasses work around the definition of stub compile and mode
#
# attributes in the common base class AbstractCompileMode. If it
# # These subclasses work around the definition of stub compile and mode
# didn't define new attributes, then the stub code inherited via
# # attributes in the common base class AbstractCompileMode. If it
# RestrictedCompileMode would override the real definitions in
# # didn't define new attributes, then the stub code inherited via
# Expression.
# # RestrictedCompileMode would override the real definitions in
# # Expression.
class
RExpression
(
RestrictedCompileMode
,
Expression
):
#
mode
=
"eval"
# class RExpression(RestrictedCompileMode, Expression):
CodeGeneratorClass
=
RestrictedExpressionCodeGenerator
# mode = "eval"
# CodeGeneratorClass = RestrictedExpressionCodeGenerator
class
RInteractive
(
RestrictedCompileMode
,
Interactive
):
#
mode
=
"single"
# class RInteractive(RestrictedCompileMode, Interactive):
CodeGeneratorClass
=
RestrictedInteractiveCodeGenerator
# mode = "single"
# CodeGeneratorClass = RestrictedInteractiveCodeGenerator
class
RModule
(
RestrictedCompileMode
,
Module
):
#
mode
=
"exec"
# class RModule(RestrictedCompileMode, Module):
CodeGeneratorClass
=
RestrictedModuleCodeGenerator
# mode = "exec"
# CodeGeneratorClass = RestrictedModuleCodeGenerator
class
RFunction
(
RModule
):
#
"""A restricted Python function built from parts."""
# class RFunction(RModule):
# """A restricted Python function built from parts."""
CodeGeneratorClass
=
RestrictedModuleCodeGenerator
#
# CodeGeneratorClass = RestrictedModuleCodeGenerator
def
__init__
(
self
,
p
,
body
,
name
,
filename
,
globals
):
#
self
.
params
=
p
# def __init__(self, p, body, name, filename, globals):
if
body
:
# self.params = p
body
=
'
\
n
'
.
join
(
body
.
splitlines
())
+
'
\
n
'
# if body:
self
.
body
=
body
# body = '\n'.join(body.splitlines()) + '\n'
self
.
name
=
name
# self.body = body
self
.
globals
=
globals
or
[]
# self.name = name
RModule
.
__init__
(
self
,
None
,
filename
)
# self.globals = globals or []
# RModule.__init__(self, None, filename)
def
parse
(
self
):
#
# Parse the parameters and body, then combine them.
# def parse(self):
firstline
=
'def f(%s): pass'
%
self
.
params
# # Parse the parameters and body, then combine them.
tree
=
niceParse
(
firstline
,
'<function parameters>'
,
'exec'
)
# firstline = 'def f(%s): pass' % self.params
f
=
tree
.
node
.
nodes
[
0
]
# tree = niceParse(firstline, '<function parameters>', 'exec')
body_code
=
niceParse
(
self
.
body
,
self
.
filename
,
'exec'
)
# f = tree.node.nodes[0]
# Stitch the body code into the function.
# body_code = niceParse(self.body, self.filename, 'exec')
f
.
code
.
nodes
=
body_code
.
node
.
nodes
# # Stitch the body code into the function.
f
.
name
=
self
.
name
# f.code.nodes = body_code.node.nodes
# Look for a docstring, if there are any nodes at all
# f.name = self.name
if
len
(
f
.
code
.
nodes
)
>
0
:
# # Look for a docstring, if there are any nodes at all
stmt1
=
f
.
code
.
nodes
[
0
]
# if len(f.code.nodes) > 0:
if
(
isinstance
(
stmt1
,
ast
.
Discard
)
and
# stmt1 = f.code.nodes[0]
isinstance
(
stmt1
.
expr
,
ast
.
Const
)
and
# if (isinstance(stmt1, ast.Discard) and
isinstance
(
stmt1
.
expr
.
value
,
str
)):
# isinstance(stmt1.expr, ast.Const) and
f
.
doc
=
stmt1
.
expr
.
value
# isinstance(stmt1.expr.value, str)):
# The caller may specify that certain variables are globals
# f.doc = stmt1.expr.value
# so that they can be referenced before a local assignment.
# # The caller may specify that certain variables are globals
# The only known example is the variables context, container,
# # so that they can be referenced before a local assignment.
# script, traverse_subpath in PythonScripts.
# # The only known example is the variables context, container,
if
self
.
globals
:
# # script, traverse_subpath in PythonScripts.
f
.
code
.
nodes
.
insert
(
0
,
ast
.
Global
(
self
.
globals
))
# if self.globals:
return
tree
# f.code.nodes.insert(0, ast.Global(self.globals))
# return tree
src/RestrictedPython/SelectCompiler.py
View file @
2ebcf9c5
...
@@ -14,10 +14,10 @@
...
@@ -14,10 +14,10 @@
"""
"""
# Use the compiler from the standard library.
# Use the compiler from the standard library.
import
compiler
#
import compiler
from
compiler
import
ast
#
from compiler import ast
from
compiler.transformer
import
parse
#
from compiler.transformer import parse
from
compiler.consts
import
OP_ASSIGN
,
OP_DELETE
,
OP_APPLY
#
from compiler.consts import OP_ASSIGN, OP_DELETE, OP_APPLY
from
RCompile
import
\
from
RCompile
import
\
compile_restricted
,
\
compile_restricted
,
\
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment