-
Notifications
You must be signed in to change notification settings - Fork 38
/
test_compile.py
240 lines (191 loc) · 7.41 KB
/
test_compile.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
from RestrictedPython import compile_restricted
from RestrictedPython import CompileResult
from RestrictedPython._compat import IS_PY2
from RestrictedPython._compat import IS_PY3
from tests import c_eval
from tests import c_exec
from tests import c_single
from tests import e_eval
import platform
import pytest
import types
def test_compile__compile_restricted_invalid_code_input():
with pytest.raises(TypeError):
compile_restricted(object(), '<string>', 'exec')
with pytest.raises(TypeError):
compile_restricted(object(), '<string>', 'eval')
with pytest.raises(TypeError):
compile_restricted(object(), '<string>', 'single')
def test_compile__compile_restricted_invalid_policy_input():
with pytest.raises(TypeError):
compile_restricted("pass", '<string>', 'exec', policy=object)
def test_compile__compile_restricted_invalid_mode_input():
with pytest.raises(TypeError):
compile_restricted("pass", '<string>', 'invalid')
INVALID_ASSINGMENT = """
1 = 2
"""
def test_compile__invalid_syntax():
with pytest.raises(SyntaxError) as err:
compile_restricted(INVALID_ASSINGMENT, '<string>', 'exec')
assert "can't assign to literal at statement:" in str(err.value)
@pytest.mark.parametrize(*c_exec)
def test_compile__compile_restricted_exec__1(c_exec):
"""It returns a CompileResult on success."""
result = c_exec('a = 42')
assert result.__class__ == CompileResult
assert result.errors == ()
assert result.warnings == []
assert result.used_names == {}
glob = {}
exec(result.code, glob)
assert glob['a'] == 42
@pytest.mark.parametrize(*c_exec)
def test_compile__compile_restricted_exec__2(c_exec):
"""It compiles without restrictions if there is no policy."""
result = c_exec('_a = 42', policy=None)
assert result.errors == ()
assert result.warnings == []
assert result.used_names == {}
glob = {}
exec(result.code, glob)
assert glob['_a'] == 42
@pytest.mark.parametrize(*c_exec)
def test_compile__compile_restricted_exec__3(c_exec):
"""It returns a tuple of errors if the code is not allowed.
There is no code in this case.
"""
result = c_exec('_a = 42\n_b = 43')
errors = (
'Line 1: "_a" is an invalid variable name because it starts with "_"',
'Line 2: "_b" is an invalid variable name because it starts with "_"')
assert result.errors == errors
assert result.warnings == []
assert result.used_names == {}
assert result.code is None
@pytest.mark.parametrize(*c_exec)
def test_compile__compile_restricted_exec__4(c_exec):
"""It does not return code on a SyntaxError."""
result = c_exec('asdf|')
assert result.code is None
assert result.warnings == []
assert result.used_names == {}
assert result.errors == (
"Line 1: SyntaxError: invalid syntax at statement: 'asdf|'",)
@pytest.mark.parametrize(*c_exec)
def test_compile__compile_restricted_exec__5(c_exec):
"""It does not return code if the code contains a NULL byte."""
result = c_exec('a = 5\x00')
assert result.code is None
assert result.warnings == []
assert result.used_names == {}
if IS_PY2:
assert result.errors == (
'compile() expected string without null bytes',)
else:
assert result.errors == (
'source code string cannot contain null bytes',)
EXEC_STATEMENT = """\
def no_exec():
exec 'q = 1'
"""
@pytest.mark.skipif(
IS_PY2,
reason="exec statement in Python 2 is handled by RestrictedPython ")
@pytest.mark.parametrize(*c_exec)
def test_compile__compile_restricted_exec__10(c_exec):
"""It is a SyntaxError to use the `exec` statement. (Python 3 only)"""
result = c_exec(EXEC_STATEMENT)
assert (
'Line 2: SyntaxError: Missing parentheses in call to \'exec\' at '
'statement: "exec \'q = 1\'"',) == result.errors
FUNCTION_DEF = """\
def a():
pass
"""
@pytest.mark.parametrize(*c_eval)
def test_compile__compile_restricted_eval__1(c_eval):
"""It compiles code as an Expression.
Function definitions are not allowed in Expressions.
"""
result = c_eval(FUNCTION_DEF)
assert result.errors == (
"Line 1: SyntaxError: invalid syntax at statement: 'def a():'",)
@pytest.mark.parametrize(*e_eval)
def test_compile__compile_restricted_eval__2(e_eval):
"""It compiles code as an Expression."""
assert e_eval('4 * 6') == 24
@pytest.mark.parametrize(*c_eval)
def test_compile__compile_restricted_eval__used_names(c_eval):
result = c_eval("a + b + func(x)")
assert result.errors == ()
assert result.warnings == []
assert result.used_names == {'a': True, 'b': True, 'x': True, 'func': True}
@pytest.mark.parametrize(*c_single)
def test_compile__compile_restricted_csingle(c_single):
"""It compiles code as an Interactive."""
result = c_single('4 * 6')
assert result.code is None
assert result.errors == (
'Line None: Interactive statements are not allowed.',
)
PRINT_EXAMPLE = """
def a():
print 'Hello World!'
"""
@pytest.mark.skipif(
IS_PY3,
reason="Print statement is gone in Python 3."
"Test Deprecation Warming in Python 2")
def test_compile_restricted():
"""This test checks compile_restricted itself if that emit Python warnings.
For actual tests for print statement see: test_print_stmt.py
"""
with pytest.warns(SyntaxWarning) as record:
result = compile_restricted(PRINT_EXAMPLE, '<string>', 'exec')
assert isinstance(result, types.CodeType)
# Non-CPython versions have a RuntimeWarning, too.
if len(record) > 2: # pragma: no cover
record.pop()
assert len(record) == 2
assert record[0].message.args[0] == \
'Line 3: Print statement is deprecated ' \
'and not available anymore in Python 3.'
assert record[1].message.args[0] == \
"Line 2: Prints, but never reads 'printed' variable."
EVAL_EXAMPLE = """
def a():
eval('2 + 2')
"""
def test_compile_restricted_eval():
"""This test checks compile_restricted itself if that raise Python errors.
"""
with pytest.raises(SyntaxError,
message="Line 3: Eval calls are not allowed."):
compile_restricted(EVAL_EXAMPLE, '<string>', 'exec')
def test_compile___compile_restricted_mode__1(recwarn, mocker):
"""It warns when using another Python implementation than CPython."""
if platform.python_implementation() == 'CPython':
# Using CPython we have to fake the check:
mocker.patch('RestrictedPython.compile.IS_CPYTHON', new=False)
compile_restricted('42')
assert len(recwarn) == 1
w = recwarn.pop()
assert w.category == RuntimeWarning
assert str(w.message) == str(
'RestrictedPython is only supported on CPython: use on other Python '
'implementations may create security issues.'
)
@pytest.mark.skipif(
platform.python_implementation() == 'CPython',
reason='Warning only present if not CPython.')
def test_compile_CPython_warning(recwarn, mocker):
"""It warns when using another Python implementation than CPython."""
assert platform.python_implementation() != 'CPython'
with pytest.warns(RuntimeWarning) as record:
compile_restricted('42')
assert len(record) == 1
assert str(record[0].message) == str(
'RestrictedPython is only supported on CPython: use on other Python '
'implementations may create security issues.'
)