Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 15 additions & 21 deletions src/RestrictedPython/transformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,7 @@ def generic_visit(self, node):
# )
self.warn(
node,
'{o.__class__.__name__}'
'{0.__class__.__name__}'
' statement is not known to RestrictedPython'.format(node)
)
self.not_allowed(node)
Expand All @@ -515,43 +515,36 @@ def visit_Num(self, node):
return self.node_contents_visit(node)

def visit_Str(self, node):
"""Allow strings without restrictions."""
"""Allow string literals without restrictions."""
return self.node_contents_visit(node)

def visit_Bytes(self, node):
"""Allow bytes without restrictions.
"""Allow bytes literals without restrictions.

Bytes is Python 3 only.
"""
self.not_allowed(node)
return self.node_contents_visit(node)

def visit_List(self, node):
"""

"""
"""Allow list literals without restrictions."""
return self.node_contents_visit(node)

def visit_Tuple(self, node):
"""

"""
"""Allow tuple literals without restrictions."""
return self.node_contents_visit(node)

def visit_Set(self, node):
"""

"""
self.not_allowed(node)
"""Allow set literals without restrictions."""
return self.node_contents_visit(node)

def visit_Dict(self, node):
"""

"""
"""Allow dict literals without restrictions."""
return self.node_contents_visit(node)

def visit_Ellipsis(self, node):
"""
"""Deny using `...`.

Ellipsis is exists only in Python 3.
"""
self.not_allowed(node)

Expand Down Expand Up @@ -620,15 +613,16 @@ def visit_Starred(self, node):
return self.node_contents_visit(node)

# Expressions

def visit_Expression(self, node):
"""Allow Expression statements without restrictions.
Python 2 only AST Element.

They are in the AST when using the `eval` compile mode.
"""
return self.node_contents_visit(node)

def visit_Expr(self, node):
"""Allow Expr statements without restrictions.
Python 3+ AST Element."""
"""Allow Expr statements (any expression) without restrictions."""
return self.node_contents_visit(node)

def visit_UnaryOp(self, node):
Expand Down
31 changes: 27 additions & 4 deletions tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,48 @@
import RestrictedPython


def _compile(compile_func, source):
"""Compile some source with a compile func."""
result = compile_func(source)
assert result.errors == (), result.errors
assert result.code is not None
return result.code


def _execute(compile_func):
"""Factory to create an execute function."""
def _execute(source, glb=None):
result = compile_func(source)
assert result.errors == (), result.errors
assert result.code is not None
code = _compile(compile_func, source)
if glb is None:
glb = {}
exec(result.code, glb)
exec(code, glb)
return glb
return _execute


def _eval(compile_func):
"""Factory to create an eval function."""
def _eval(source, glb=None):
code = _compile(compile_func, source)
if glb is None:
glb = {}
return eval(code, glb)
return _eval


# Define the arguments for @pytest.mark.parametrize to be able to test both the
# old and the new implementation to be equal:
compile = ('compile', [RestrictedPython.compile.compile_restricted_exec])
compile_eval = ('compile_eval',
[RestrictedPython.compile.compile_restricted_eval])
execute = ('execute',
[_execute(RestrictedPython.compile.compile_restricted_exec)])
r_eval = ('r_eval',
[_eval(RestrictedPython.compile.compile_restricted_eval)])

if IS_PY2:
from RestrictedPython import RCompile
compile[1].append(RCompile.compile_restricted_exec)
compile_eval[1].append(RCompile.compile_restricted_eval)
execute[1].append(_execute(RCompile.compile_restricted_exec))
r_eval[1].append(_eval(RCompile.compile_restricted_eval))
28 changes: 28 additions & 0 deletions tests/test_compile.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from . import compile
from . import compile_eval
from . import r_eval
from RestrictedPython import compile_restricted
from RestrictedPython import CompileResult
from RestrictedPython._compat import IS_PY2
Expand Down Expand Up @@ -114,3 +116,29 @@ def test_compile__compile_restricted_exec__10(compile):
assert (
"Line 2: SyntaxError: Missing parentheses in call to 'exec' in on "
"statement: exec 'q = 1'",) == result.errors


FUNCTION_DEF = """\
def a():
pass
"""


@pytest.mark.parametrize(*compile_eval)
def test_compile__compile_restricted_eval__1(compile_eval):
"""It compiles code as an Expression.

Function definitions are not allowed in Expressions.
"""
result = compile_eval(FUNCTION_DEF)
if compile_eval is RestrictedPython.compile.compile_restricted_eval:
assert result.errors == (
'Line 1: SyntaxError: invalid syntax in on statement: def a():',)
else:
assert result.errors == ('invalid syntax (<string>, line 1)',)


@pytest.mark.parametrize(*r_eval)
def test_compile__compile_restricted_eval__2(r_eval):
"""It compiles code as an Expression."""
assert r_eval('4 * 6') == 24
47 changes: 42 additions & 5 deletions tests/test_transformer.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,59 @@
from . import compile
from . import execute
from RestrictedPython import RestrictingNodeTransformer
from RestrictedPython._compat import IS_PY2
from RestrictedPython._compat import IS_PY3
from RestrictedPython.Guards import guarded_iter_unpack_sequence
from RestrictedPython.Guards import guarded_unpack_sequence

import ast
import contextlib
import pytest
import RestrictedPython
import types


def test_transformer__RestrictingNodeTransformer__generic_visit__1():
"""It log an error if there is an unknown ast node visited."""
class MyFancyNode(ast.AST):
pass

transformer = RestrictingNodeTransformer()
transformer.visit(MyFancyNode())
assert transformer.errors == [
'Line None: MyFancyNode statements are not allowed.']
assert transformer.warnings == [
'Line None: MyFancyNode statement is not known to RestrictedPython']


@pytest.mark.parametrize(*execute)
def test_transformer__RestrictingNodeTransformer__visit_Num__1(execute):
"""It allows to use number literals."""
glb = execute('a = 42')
assert glb['a'] == 42


@pytest.mark.parametrize(*execute)
def test_transformer__RestrictingNodeTransformer__visit_Bytes__1(execute):
"""It allows to use bytes literals."""
glb = execute('a = b"code"')
assert glb['a'] == b"code"


@pytest.mark.parametrize(*execute)
def test_transformer__RestrictingNodeTransformer__visit_Set__1(execute):
"""It allows to use bytes literals."""
glb = execute('a = {1, 2, 3}')
assert glb['a'] == set([1, 2, 3])


@pytest.mark.skipif(IS_PY2,
reason="... is new in Python 3")
@pytest.mark.parametrize(*compile)
def test_transformer__RestrictingNodeTransformer__visit_Num__1(compile):
"""It compiles a number successfully."""
result = compile('42')
assert result.errors == ()
assert str(result.code.__class__.__name__) == 'code'
def test_transformer__RestrictingNodeTransformer__visit_Ellipsis__1(compile):
"""It prevents using the `ellipsis` statement."""
result = compile('...')
assert result.errors == ('Line 1: Ellipsis statements are not allowed.',)


@pytest.mark.parametrize(*compile)
Expand Down