Skip to content

Commit 86f63a5

Browse files
loechelicemac
andauthored
enable single mode (#272)
Co-authored-by: Michael Howitz <[email protected]>
1 parent 67f3c1b commit 86f63a5

File tree

4 files changed

+78
-6
lines changed

4 files changed

+78
-6
lines changed

CHANGES.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Changes
66

77
- Allow to use the package with Python 3.13 -- Caution: No security
88
audit has been done so far.
9+
- Add support for single mode statements / execution.
910

1011

1112
7.1 (2024-03-14)

docs/usage/basic_usage.rst

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,62 @@ One common advanced usage would be to define an own restricted builtin dictionar
9494

9595
There is a shortcut for ``{'__builtins__': safe_builtins}`` named ``safe_globals`` which can be imported from ``RestrictedPython``.
9696

97+
Other Usages
98+
------------
99+
100+
RestrictedPython has similar to normal Python multiple modes:
101+
102+
* exec
103+
* eval
104+
* single
105+
* function
106+
107+
you can use it by:
108+
109+
.. testcode::
110+
111+
from RestrictedPython import compile_restricted
112+
113+
source_code = """
114+
def do_something():
115+
pass
116+
"""
117+
118+
byte_code = compile_restricted(
119+
source_code,
120+
filename='<inline code>',
121+
mode='exec'
122+
)
123+
exec(byte_code)
124+
do_something()
125+
126+
.. testcode::
127+
128+
from RestrictedPython import compile_restricted
129+
130+
byte_code = compile_restricted(
131+
"2 + 2",
132+
filename='<inline code>',
133+
mode='eval'
134+
)
135+
eval(byte_code)
136+
137+
138+
.. testcode:: single
139+
140+
from RestrictedPython import compile_restricted
141+
142+
byte_code = compile_restricted(
143+
"2 + 2",
144+
filename='<inline code>',
145+
mode='single'
146+
)
147+
exec(byte_code)
148+
149+
.. testoutput:: single
150+
151+
4
152+
97153
Necessary setup
98154
---------------
99155

src/RestrictedPython/transformer.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,10 @@ def visit_NameConstant(self, node):
593593
"""
594594
return self.node_contents_visit(node)
595595

596+
def visit_Interactive(self, node):
597+
"""Allow single mode without restrictions."""
598+
return self.node_contents_visit(node)
599+
596600
def visit_List(self, node):
597601
"""Allow list literals without restrictions."""
598602
return self.node_contents_visit(node)

tests/test_compile.py

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -160,13 +160,24 @@ def test_compile__compile_restricted_eval__used_names():
160160
assert result.used_names == {'a': True, 'b': True, 'x': True, 'func': True}
161161

162162

163-
def test_compile__compile_restricted_csingle():
163+
def test_compile__compile_restricted_single__1():
164164
"""It compiles code as an Interactive."""
165-
result = compile_restricted_single('4 * 6')
166-
assert result.code is None
167-
assert result.errors == (
168-
'Line None: Interactive statements are not allowed.',
169-
)
165+
result = compile_restricted_single('x = 4 * 6')
166+
167+
assert result.errors == ()
168+
assert result.warnings == []
169+
assert result.code is not None
170+
locals = {}
171+
exec(result.code, {}, locals)
172+
assert locals["x"] == 24
173+
174+
175+
def test_compile__compile_restricted__2():
176+
"""It compiles code as an Interactive."""
177+
code = compile_restricted('x = 4 * 6', filename="<string>", mode="single")
178+
locals = {}
179+
exec(code, {}, locals)
180+
assert locals["x"] == 24
170181

171182

172183
PRINT_EXAMPLE = """

0 commit comments

Comments
 (0)