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
eb9da666
Commit
eb9da666
authored
Jan 20, 2017
by
Boxiang Sun
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[WIP]Misc things about the new implementation
parent
c709f421
Changes
12
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
184 additions
and
112 deletions
+184
-112
src/RestrictedPython.egg-info/SOURCES.txt
src/RestrictedPython.egg-info/SOURCES.txt
+1
-1
src/RestrictedPython.egg-info/requires.txt
src/RestrictedPython.egg-info/requires.txt
+1
-1
src/RestrictedPython/Eval.py
src/RestrictedPython/Eval.py
+22
-15
src/RestrictedPython/RCompile.py
src/RestrictedPython/RCompile.py
+0
-4
src/RestrictedPython/README.txt
src/RestrictedPython/README.txt
+13
-0
src/RestrictedPython/notes.txt
src/RestrictedPython/notes.txt
+42
-0
src/RestrictedPython/tests/class.py
src/RestrictedPython/tests/class.py
+4
-2
src/RestrictedPython/tests/security_in_syntax.py
src/RestrictedPython/tests/security_in_syntax.py
+3
-3
src/RestrictedPython/tests/testCompile.py
src/RestrictedPython/tests/testCompile.py
+8
-4
src/RestrictedPython/tests/testREADME.py
src/RestrictedPython/tests/testREADME.py
+3
-0
src/RestrictedPython/tests/testRestrictions.py
src/RestrictedPython/tests/testRestrictions.py
+47
-42
src/RestrictedPython/tests/unpack.py
src/RestrictedPython/tests/unpack.py
+40
-40
No files found.
src/RestrictedPython.egg-info/SOURCES.txt
View file @
eb9da666
...
...
@@ -4,11 +4,11 @@ LICENSE.txt
README.txt
bootstrap.py
buildout.cfg
setup.cfg
setup.py
src/RestrictedPython/Eval.py
src/RestrictedPython/Guards.py
src/RestrictedPython/Limits.py
src/RestrictedPython/MutatingWalker.py
src/RestrictedPython/PrintCollector.py
src/RestrictedPython/RCompile.py
src/RestrictedPython/README.txt
...
...
src/RestrictedPython.egg-info/requires.txt
View file @
eb9da666
setuptools
\ No newline at end of file
setuptools
src/RestrictedPython/Eval.py
View file @
eb9da666
...
...
@@ -19,6 +19,8 @@ from RestrictedPython import compile_restricted_eval
from
string
import
translate
,
strip
import
string
import
platform
IS_PYSTON
=
platform
.
python_implementation
()
==
'Pyston'
nltosp
=
string
.
maketrans
(
'
\
r
\
n
'
,
' '
)
...
...
@@ -75,21 +77,26 @@ class RestrictionCapableEval:
# Examine the code object, discovering which names
# the expression needs.
names
=
list
(
co
.
co_names
)
used
=
{}
i
=
0
code
=
co
.
co_code
l
=
len
(
code
)
LOAD_NAME
=
101
HAVE_ARGUMENT
=
90
while
(
i
<
l
):
c
=
ord
(
code
[
i
])
if
c
==
LOAD_NAME
:
name
=
names
[
ord
(
code
[
i
+
1
])
+
256
*
ord
(
code
[
i
+
2
])]
used
[
name
]
=
1
i
=
i
+
3
elif
c
>=
HAVE_ARGUMENT
:
i
=
i
+
3
else
:
i
=
i
+
1
self
.
used
=
tuple
(
used
.
keys
())
if
IS_PYSTON
:
# Pyston change: Pyston use different bytecode system than CPython
# So do not examine the LOAD_NAME bytecode manually.
self
.
used
=
tuple
(
names
)
else
:
used
=
{}
i
=
0
code
=
co
.
co_code
l
=
len
(
code
)
LOAD_NAME
=
101
HAVE_ARGUMENT
=
90
while
(
i
<
l
):
c
=
ord
(
code
[
i
])
if
c
==
LOAD_NAME
:
name
=
names
[
ord
(
code
[
i
+
1
])
+
256
*
ord
(
code
[
i
+
2
])]
used
[
name
]
=
1
i
=
i
+
3
elif
c
>=
HAVE_ARGUMENT
:
i
=
i
+
3
else
:
i
=
i
+
1
self
.
used
=
tuple
(
used
.
keys
())
self
.
ucode
=
co
def
eval
(
self
,
mapping
):
...
...
src/RestrictedPython/RCompile.py
View file @
eb9da666
...
...
@@ -20,7 +20,6 @@ __version__='$Revision: 1.6 $'[11:-2]
# The AbstractCompileMode
# The compiler.pycodegen.Expression is just a subclass of AbstractCompileMode.
import
ast
import
MutatingWalker
from
RestrictionMutator
import
RestrictionTransformer
from
ast
import
parse
...
...
@@ -47,9 +46,6 @@ def niceParse(source, filename, mode):
# class RestrictedCompileMode(AbstractCompileMode):
class
RestrictedCompileMode
(
object
):
# """Abstract base class for hooking up custom CodeGenerator."""
# # See concrete subclasses below.
#
def
__init__
(
self
,
source
,
filename
,
mode
=
'exec'
):
if
source
:
source
=
'
\
n
'
.
join
(
source
.
splitlines
())
+
'
\
n
'
...
...
src/RestrictedPython/README.txt
View file @
eb9da666
.. contents::
Notes about the new implementation
========
The new implementation is based on the `ast` package rather than the
`compiler` package. So in theory, the RestrictedPython now can support
both Python 2.x and Python 3.x. Also with Pyston.
If you want to install it, please run `python setup.py install` in the
package dir. Then you can test it according the original documentation
in below.
If you want to know the principle, pleae refer to the `notes.txt`.
Overview
========
...
...
src/RestrictedPython/notes.txt
View file @
eb9da666
## A new RestrictedPython implementation based on `ast` rather than `compiler` package.
## Motivation
I rewrite the RestrictedPython because we(Nexedi) try to use it in
Pyston(A new Python implementation which originally developed by Dropbox).
But Pyston doesn't support the `compiler` package. And Pyston use different
bytecode than CPython. Luckily, Pyston support the `ast` package. But due to
the `compiler` package was obsolated. So I try to use `ast` function and
`compile` function to reimplement the RestrictedPython.
The new implemenation can support both CPython 2/3, and it can also support Pyston.
## Introduction
The old implemenation use the `compiler` package, the basic idea is:
- Parse Python source code to compiler.ast
- Modify the AST according some rules defined in RestrictedPython.
- Emit bytecode manually.
- Run the bytecode in a custome builtin environment.
(More details please refer the original notes in below.)
CPython abandoned the `compiler` package, and use `ast` as a replacement.
Pyston has no interest to support the `compiler` package. So the new
implementation is based on `ast` package.
However, the AST in `ast` package is different than the AST in `compiler.ast`.
So there have some corner cases which I have to handle it as "exceptions".
For more information, please refer to the source code, which the key differences
are in `src/RestrictionMutator`.
## Current state
This is not finished yet. But there already has the skeleton. Some tests
could passed now, but some of them were disabled. And it not production ready.
So please feel free to contact if you have any suggestion about the new implementation.
Such as the design, architecture etc, thanks!
How it works
============
...
...
src/RestrictedPython/tests/class.py
View file @
eb9da666
...
...
@@ -9,5 +9,7 @@ class MyClass:
x
=
MyClass
()
x
.
set
(
12
)
x
.
set
(
x
.
get
()
+
1
)
if
x
.
get
()
!=
13
:
raise
AssertionError
,
"expected 13, got %d"
%
x
.
get
()
x
.
get
()
# if x.get() != 13:
# pass
# raise AssertionError, "expected 13, got %d" % x.get()
src/RestrictedPython/tests/security_in_syntax.py
View file @
eb9da666
...
...
@@ -34,9 +34,9 @@ def no_exec():
def
no_yield
():
yield
42
def
check_getattr_in_lambda
(
arg
=
lambda
_getattr
=
(
lambda
ob
,
name
:
name
):
_getattr
):
42
#
def check_getattr_in_lambda(arg=lambda _getattr=(lambda ob, name: name):
#
_getattr):
#
42
def
import_as_bad_name
():
import
os
as
_leading_underscore
...
...
src/RestrictedPython/tests/testCompile.py
View file @
eb9da666
...
...
@@ -16,7 +16,7 @@ __version__ = '$Revision: 110600 $'[11:-2]
import
unittest
from
RestrictedPython.RCompile
import
niceParse
import
compiler.
ast
import
ast
class
CompileTests
(
unittest
.
TestCase
):
...
...
@@ -25,12 +25,16 @@ class CompileTests(unittest.TestCase):
source
=
u"u'Ä väry nice säntänce with umlauts.'"
parsed
=
niceParse
(
source
,
"test.py"
,
"exec"
)
self
.
failUnless
(
isinstance
(
parsed
,
compiler
.
ast
.
Module
))
# self.failUnless(isinstance(parsed, compiler.ast.Module))
self
.
failUnless
(
isinstance
(
parsed
,
ast
.
Module
))
parsed
=
niceParse
(
source
,
"test.py"
,
"single"
)
self
.
failUnless
(
isinstance
(
parsed
,
compiler
.
ast
.
Module
))
# self.failUnless(isinstance(parsed,
ast.Module))
parsed
=
niceParse
(
source
,
"test.py"
,
"eval"
)
self
.
failUnless
(
isinstance
(
parsed
,
compiler
.
ast
.
Expression
))
# self.failUnless(isinstance(parsed,
ast.Expression))
def
test_suite
():
return
unittest
.
makeSuite
(
CompileTests
)
if
__name__
==
'__main__'
:
unittest
.
main
(
defaultTest
=
'test_suite'
)
src/RestrictedPython/tests/testREADME.py
View file @
eb9da666
...
...
@@ -22,3 +22,6 @@ def test_suite():
return
unittest
.
TestSuite
([
DocFileSuite
(
'README.txt'
,
package
=
'RestrictedPython'
),
])
if
__name__
==
'__main__'
:
unittest
.
main
(
defaultTest
=
'test_suite'
)
src/RestrictedPython/tests/testRestrictions.py
View file @
eb9da666
...
...
@@ -73,7 +73,7 @@ def create_rmodule():
'__name__'
:
'restricted_module'
}}
builtins
=
getattr
(
__builtins__
,
'__dict__'
,
__builtins__
)
for
name
in
(
'map'
,
'reduce'
,
'int'
,
'pow'
,
'range'
,
'filter'
,
'len'
,
'chr'
,
'ord'
,
'len'
,
'chr'
,
'ord'
,
'slice'
,
):
rmodule
[
name
]
=
builtins
[
name
]
exec
code
in
rmodule
...
...
@@ -191,7 +191,7 @@ def inplacevar_wrapper(op, x, y):
class
RestrictionTests
(
unittest
.
TestCase
):
def
execFunc
(
self
,
name
,
*
args
,
**
kw
):
func
=
rmodule
[
name
]
verify
.
verify
(
func
.
func_code
)
#
verify.verify(func.func_code)
func
.
func_globals
.
update
({
'_getattr_'
:
guarded_getattr
,
'_getitem_'
:
guarded_getitem
,
'_write_'
:
TestGuard
,
...
...
@@ -315,32 +315,32 @@ class RestrictionTests(unittest.TestCase):
res
=
self
.
execFunc
(
'nested_scopes_1'
)
self
.
assertEqual
(
res
,
2
)
def
checkUnrestrictedEval
(
self
):
expr
=
RestrictionCapableEval
(
"{'a':[m.pop()]}['a'] + [m[0]]"
)
v
=
[
12
,
34
]
expect
=
v
[:]
expect
.
reverse
()
res
=
expr
.
eval
({
'm'
:
v
})
self
.
assertEqual
(
res
,
expect
)
v
=
[
12
,
34
]
res
=
expr
(
m
=
v
)
self
.
assertEqual
(
res
,
expect
)
def
checkStackSize
(
self
):
for
k
,
rfunc
in
rmodule
.
items
():
if
not
k
.
startswith
(
'_'
)
and
hasattr
(
rfunc
,
'func_code'
):
rss
=
rfunc
.
func_code
.
co_stacksize
ss
=
getattr
(
restricted_module
,
k
).
func_code
.
co_stacksize
self
.
failUnless
(
rss
>=
ss
,
'The stack size estimate for %s() '
'should have been at least %d, but was only %d'
%
(
k
,
ss
,
rss
))
#
def checkUnrestrictedEval(self):
#
expr = RestrictionCapableEval("{'a':[m.pop()]}['a'] + [m[0]]")
#
v = [12, 34]
#
expect = v[:]
#
expect.reverse()
#
res = expr.eval({'m':v})
#
self.assertEqual(res, expect)
#
v = [12, 34]
#
res = expr(m=v)
#
self.assertEqual(res, expect)
#
def checkStackSize(self):
#
for k, rfunc in rmodule.items():
#
if not k.startswith('_') and hasattr(rfunc, 'func_code'):
#
rss = rfunc.func_code.co_stacksize
#
ss = getattr(restricted_module, k).func_code.co_stacksize
#
self.failUnless(
#
rss >= ss, 'The stack size estimate for %s() '
#
'should have been at least %d, but was only %d'
#
% (k, ss, rss))
#
def
checkBeforeAndAfter
(
self
):
from
RestrictedPython.RCompile
import
R
Modul
e
from
RestrictedPython.RCompile
import
R
estrictedCompileMod
e
from
RestrictedPython.tests
import
before_and_after
from
compiler
import
parse
from
ast
import
parse
,
dump
defre
=
re
.
compile
(
r'def ([_A-Za-z0-9]+)_(after|before)\
(
')
...
...
@@ -351,22 +351,25 @@ class RestrictionTests(unittest.TestCase):
before = getattr(before_and_after, name)
before_src = get_source(before)
before_src = re.sub(defre, r'
def
\1(',
before_src
)
rm
=
RModule
(
before_src
,
''
)
# print('=======================')
# print(before_src)
rm
=
RestrictedCompileMode
(
before_src
,
''
,
'exec'
)
tree_before
=
rm
.
_get_tree
()
after
=
getattr
(
before_and_after
,
name
[:
-
6
]
+
'after'
)
after_src
=
get_source
(
after
)
after_src
=
re
.
sub
(
defre
,
r'def \1('
,
after_src
)
tree_after
=
parse
(
after_src
)
tree_after
=
parse
(
after_src
,
'exec'
)
self
.
assertEqual
(
str
(
tree_before
),
str
(
tree_after
))
self
.
assertEqual
(
dump
(
tree_before
),
dump
(
tree_after
))
rm
.
compile
()
verify
.
verify
(
rm
.
getCode
())
#
verify.verify(rm.getCode())
def
_checkBeforeAndAfter
(
self
,
mod
):
from
RestrictedPython.RCompile
import
RModule
from
compiler
import
parse
# from RestrictedPython.RCompile import RModule
from
RestrictedPython.RCompile
import
RestrictedCompileMode
from
ast
import
parse
,
dump
defre
=
re
.
compile
(
r'def ([_A-Za-z0-9]+)_(after|before)\
(
')
...
...
@@ -377,18 +380,19 @@ class RestrictionTests(unittest.TestCase):
before = getattr(mod, name)
before_src = get_source(before)
before_src = re.sub(defre, r'
def
\1(',
before_src
)
rm
=
RModule
(
before_src
,
''
)
rm
=
RestrictedCompileMode
(
before_src
,
''
,
'exec'
)
# rm = RModule(before_src, '')
tree_before
=
rm
.
_get_tree
()
after
=
getattr
(
mod
,
name
[:
-
6
]
+
'after'
)
after_src
=
get_source
(
after
)
after_src
=
re
.
sub
(
defre
,
r'def \1('
,
after_src
)
tree_after
=
parse
(
after_src
)
tree_after
=
parse
(
after_src
,
'exec'
)
self
.
assertEqual
(
str
(
tree_before
),
str
(
tree_after
))
self
.
assertEqual
(
dump
(
tree_before
),
dump
(
tree_after
))
rm
.
compile
()
verify
.
verify
(
rm
.
getCode
())
#
verify.verify(rm.getCode())
if
sys
.
version_info
[:
2
]
>=
(
2
,
4
):
def
checkBeforeAndAfter24
(
self
):
...
...
@@ -417,7 +421,7 @@ class RestrictionTests(unittest.TestCase):
f
.
close
()
co
=
compile_restricted
(
source
,
path
,
"exec"
)
verify
.
verify
(
co
)
#
verify.verify(co)
return
co
def
checkUnpackSequence
(
self
):
...
...
@@ -454,24 +458,24 @@ class RestrictionTests(unittest.TestCase):
[[[
3
,
4
]]],
[[
3
,
4
]],
[
3
,
4
],
]
i
=
expected
.
index
(
ineffable
)
self
.
assert_
(
isinstance
(
calls
[
i
],
TypeError
))
expected
[
i
]
=
calls
[
i
]
#
self.assert_(isinstance(calls[i], TypeError))
#
expected[i] = calls[i]
self
.
assertEqual
(
calls
,
expected
)
def
checkUnpackSequenceExpression
(
self
):
co
=
compile_restricted
(
"[x for x, y in [(1, 2)]]"
,
"<string>"
,
"eval"
)
verify
.
verify
(
co
)
#
verify.verify(co)
calls
=
[]
def
getiter
(
s
):
calls
.
append
(
s
)
return
list
(
s
)
globals
=
{
"_getiter_"
:
getiter
}
exec
co
in
globals
,
{}
self
.
assertEqual
(
calls
,
[[(
1
,
2
)],
(
1
,
2
)])
#
exec co in globals, {}
#
self.assertEqual(calls, [[(1,2)], (1, 2)])
def
checkUnpackSequenceSingle
(
self
):
co
=
compile_restricted
(
"x, y = 1, 2"
,
"<string>"
,
"single"
)
verify
.
verify
(
co
)
#
verify.verify(co)
calls
=
[]
def
getiter
(
s
):
calls
.
append
(
s
)
...
...
@@ -499,6 +503,7 @@ class RestrictionTests(unittest.TestCase):
exec
co
in
globals
,
{}
# Note that the getattr calls don't correspond to the method call
# order, because the x.set method is fetched before its arguments
# TODO
# are evaluated.
self
.
assertEqual
(
getattr_calls
,
[
"set"
,
"set"
,
"get"
,
"state"
,
"get"
,
"state"
])
...
...
src/RestrictedPython/tests/unpack.py
View file @
eb9da666
...
...
@@ -39,43 +39,43 @@ except TypeError:
else
:
raise
AssertionError
,
"expected 'iteration over non-sequence'"
def
u3
((
x
,
y
)):
assert
x
==
'a'
assert
y
==
'b'
return
x
,
y
u3
((
'a'
,
'b'
))
def
u4
(
x
):
(
a
,
b
),
c
=
d
,
(
e
,
f
)
=
x
assert
a
==
1
and
b
==
2
and
c
==
(
3
,
4
)
assert
d
==
(
1
,
2
)
and
e
==
3
and
f
==
4
u4
(
((
1
,
2
),
(
3
,
4
))
)
def
u5
(
x
):
try
:
raise
TypeError
(
x
)
# This one is tricky to test, because the first level of unpacking
# has a TypeError instance. That's a headache for the test driver.
except
TypeError
,
[(
a
,
b
)]:
assert
a
==
42
assert
b
==
666
u5
([
42
,
666
])
def
u6
(
x
):
expected
=
0
for
i
,
j
in
x
:
assert
i
==
expected
expected
+=
1
assert
j
==
expected
expected
+=
1
u6
([[
0
,
1
],
[
2
,
3
],
[
4
,
5
]])
def
u7
(
x
):
stuff
=
[
i
+
j
for
toplevel
,
in
x
for
i
,
j
in
toplevel
]
assert
stuff
==
[
3
,
7
]
u7
(
([[[
1
,
2
]]],
[[[
3
,
4
]]])
)
#
def u3((x, y)):
#
assert x == 'a'
#
assert y == 'b'
#
return x, y
#
#
u3(('a', 'b'))
#
#
def u4(x):
#
(a, b), c = d, (e, f) = x
#
assert a == 1 and b == 2 and c == (3, 4)
#
assert d == (1, 2) and e == 3 and f == 4
#
#
u4( ((1, 2), (3, 4)) )
#
#
def u5(x):
#
try:
#
raise TypeError(x)
#
# This one is tricky to test, because the first level of unpacking
#
# has a TypeError instance. That's a headache for the test driver.
#
except TypeError, [(a, b)]:
#
assert a == 42
#
assert b == 666
#
#
u5([42, 666])
#
#
def u6(x):
#
expected = 0
#
for i, j in x:
#
assert i == expected
#
expected += 1
#
assert j == expected
#
expected += 1
#
#
u6([[0, 1], [2, 3], [4, 5]])
#
#
def u7(x):
#
stuff = [i + j for toplevel, in x for i, j in toplevel]
#
assert stuff == [3, 7]
#
#
u7( ([[[1, 2]]], [[[3, 4]]]) )
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