RestrictedPython 8.3__py3-none-any.whl → 8.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- RestrictedPython/Eval.py +31 -17
- RestrictedPython/Guards.py +12 -1
- RestrictedPython/Limits.py +23 -4
- RestrictedPython/PrintCollector.py +4 -4
- RestrictedPython/Utilities.py +21 -6
- RestrictedPython/_types.py +25 -0
- RestrictedPython/compile.py +104 -61
- RestrictedPython/py.typed +0 -0
- RestrictedPython/transformer.py +175 -127
- {restrictedpython-8.3.dist-info → restrictedpython-8.4.dist-info}/METADATA +17 -1
- restrictedpython-8.4.dist-info/RECORD +16 -0
- restrictedpython-8.3.dist-info/RECORD +0 -14
- {restrictedpython-8.3.dist-info → restrictedpython-8.4.dist-info}/WHEEL +0 -0
- {restrictedpython-8.3.dist-info → restrictedpython-8.4.dist-info}/licenses/LICENSE.txt +0 -0
- {restrictedpython-8.3.dist-info → restrictedpython-8.4.dist-info}/top_level.txt +0 -0
RestrictedPython/Eval.py
CHANGED
|
@@ -13,8 +13,12 @@
|
|
|
13
13
|
"""Restricted Python Expressions."""
|
|
14
14
|
|
|
15
15
|
import ast
|
|
16
|
+
import collections
|
|
17
|
+
import types
|
|
18
|
+
import typing
|
|
16
19
|
|
|
17
|
-
from .
|
|
20
|
+
from RestrictedPython._types import cast_not_none
|
|
21
|
+
from RestrictedPython.compile import compile_restricted_eval
|
|
18
22
|
|
|
19
23
|
|
|
20
24
|
nltosp = str.maketrans('\r\n', ' ')
|
|
@@ -22,13 +26,21 @@ nltosp = str.maketrans('\r\n', ' ')
|
|
|
22
26
|
# No restrictions.
|
|
23
27
|
default_guarded_getattr = getattr
|
|
24
28
|
|
|
29
|
+
_T = typing.TypeVar('_T')
|
|
30
|
+
_TK = typing.TypeVar('_TK', contravariant=True)
|
|
31
|
+
_TV = typing.TypeVar('_TV', covariant=True)
|
|
25
32
|
|
|
26
|
-
|
|
33
|
+
|
|
34
|
+
class _GetItem(typing.Protocol[_TK, _TV]):
|
|
35
|
+
def __getitem__(self, key: _TK) -> _TV: ...
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def default_guarded_getitem(ob: _GetItem[_TK, _TV], index: _TK) -> _TV:
|
|
27
39
|
# No restrictions.
|
|
28
40
|
return ob[index]
|
|
29
41
|
|
|
30
42
|
|
|
31
|
-
def default_guarded_getiter(ob):
|
|
43
|
+
def default_guarded_getiter(ob: _T) -> _T:
|
|
32
44
|
# No restrictions.
|
|
33
45
|
return ob
|
|
34
46
|
|
|
@@ -36,17 +48,18 @@ def default_guarded_getiter(ob):
|
|
|
36
48
|
class RestrictionCapableEval:
|
|
37
49
|
"""A base class for restricted code."""
|
|
38
50
|
|
|
39
|
-
globals = {'__builtins__': None}
|
|
51
|
+
globals: dict[str, typing.Any] = {'__builtins__': None}
|
|
52
|
+
|
|
40
53
|
# restricted
|
|
41
|
-
rcode = None
|
|
54
|
+
rcode: types.CodeType | None = None
|
|
42
55
|
|
|
43
56
|
# unrestricted
|
|
44
|
-
ucode = None
|
|
57
|
+
ucode: types.CodeType | None = None
|
|
45
58
|
|
|
46
59
|
# Names used by the expression
|
|
47
|
-
used = None
|
|
60
|
+
used: tuple[str, ...] | None = None
|
|
48
61
|
|
|
49
|
-
def __init__(self, expr):
|
|
62
|
+
def __init__(self, expr: str):
|
|
50
63
|
"""Create a restricted expression
|
|
51
64
|
|
|
52
65
|
where:
|
|
@@ -60,7 +73,7 @@ class RestrictionCapableEval:
|
|
|
60
73
|
# Catch syntax errors.
|
|
61
74
|
self.prepUnrestrictedCode()
|
|
62
75
|
|
|
63
|
-
def prepRestrictedCode(self):
|
|
76
|
+
def prepRestrictedCode(self) -> None:
|
|
64
77
|
if self.rcode is None:
|
|
65
78
|
result = compile_restricted_eval(self.expr, '<string>')
|
|
66
79
|
if result.errors:
|
|
@@ -68,13 +81,12 @@ class RestrictionCapableEval:
|
|
|
68
81
|
self.used = tuple(result.used_names)
|
|
69
82
|
self.rcode = result.code
|
|
70
83
|
|
|
71
|
-
def prepUnrestrictedCode(self):
|
|
84
|
+
def prepUnrestrictedCode(self) -> None:
|
|
72
85
|
if self.ucode is None:
|
|
73
|
-
exp_node =
|
|
86
|
+
exp_node = ast.parse(
|
|
74
87
|
self.expr,
|
|
75
88
|
'<string>',
|
|
76
|
-
'eval'
|
|
77
|
-
ast.PyCF_ONLY_AST)
|
|
89
|
+
'eval')
|
|
78
90
|
|
|
79
91
|
co = compile(exp_node, '<string>', 'eval')
|
|
80
92
|
|
|
@@ -90,7 +102,9 @@ class RestrictionCapableEval:
|
|
|
90
102
|
|
|
91
103
|
self.ucode = co
|
|
92
104
|
|
|
93
|
-
def eval(self,
|
|
105
|
+
def eval(self,
|
|
106
|
+
mapping: collections.abc.Mapping[str,
|
|
107
|
+
typing.Any]) -> typing.Any:
|
|
94
108
|
# This default implementation is probably not very useful. :-(
|
|
95
109
|
# This is meant to be overridden.
|
|
96
110
|
self.prepRestrictedCode()
|
|
@@ -103,11 +117,11 @@ class RestrictionCapableEval:
|
|
|
103
117
|
|
|
104
118
|
global_scope.update(self.globals)
|
|
105
119
|
|
|
106
|
-
for name in self.used:
|
|
120
|
+
for name in cast_not_none(self.used):
|
|
107
121
|
if (name not in global_scope) and (name in mapping):
|
|
108
122
|
global_scope[name] = mapping[name]
|
|
109
123
|
|
|
110
|
-
return eval(self.rcode, global_scope)
|
|
124
|
+
return eval(cast_not_none(self.rcode), global_scope)
|
|
111
125
|
|
|
112
|
-
def __call__(self, **kw):
|
|
126
|
+
def __call__(self, **kw: typing.Any) -> typing.Any:
|
|
113
127
|
return self.eval(kw)
|
RestrictedPython/Guards.py
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
# DocumentTemplate.DT_UTil contains a few.
|
|
17
17
|
|
|
18
18
|
import builtins
|
|
19
|
+
import string
|
|
19
20
|
|
|
20
21
|
from RestrictedPython.transformer import INSPECT_ATTRIBUTES
|
|
21
22
|
|
|
@@ -220,7 +221,7 @@ def _full_write_guard():
|
|
|
220
221
|
return guard
|
|
221
222
|
|
|
222
223
|
|
|
223
|
-
full_write_guard = _full_write_guard()
|
|
224
|
+
full_write_guard = _full_write_guard() # type: ignore[no-untyped-call]
|
|
224
225
|
|
|
225
226
|
|
|
226
227
|
def guarded_setattr(object, name, value):
|
|
@@ -238,6 +239,8 @@ safe_builtins['delattr'] = guarded_delattr
|
|
|
238
239
|
|
|
239
240
|
|
|
240
241
|
raise_ = object()
|
|
242
|
+
_FORMATTER_UNSAFE_METHODS = frozenset(('format', 'get_field', 'get_value',
|
|
243
|
+
'vformat'))
|
|
241
244
|
|
|
242
245
|
|
|
243
246
|
def safer_getattr(object, name, default=None, getattr=getattr):
|
|
@@ -254,6 +257,14 @@ def safer_getattr(object, name, default=None, getattr=getattr):
|
|
|
254
257
|
(isinstance(object, type) and issubclass(object, str))):
|
|
255
258
|
raise NotImplementedError(
|
|
256
259
|
'Using the format*() methods of `str` is not safe')
|
|
260
|
+
if object is string and name == 'Formatter':
|
|
261
|
+
raise NotImplementedError('string.Formatter is not safe')
|
|
262
|
+
if name in _FORMATTER_UNSAFE_METHODS and (
|
|
263
|
+
isinstance(object, string.Formatter) or
|
|
264
|
+
(isinstance(object, type) and
|
|
265
|
+
issubclass(object, string.Formatter))):
|
|
266
|
+
raise NotImplementedError(
|
|
267
|
+
'Using string.Formatter methods is not safe')
|
|
257
268
|
if name in INSPECT_ATTRIBUTES:
|
|
258
269
|
raise AttributeError(
|
|
259
270
|
f'"{name}" is a restricted name,'
|
RestrictedPython/Limits.py
CHANGED
|
@@ -10,11 +10,28 @@
|
|
|
10
10
|
# FOR A PARTICULAR PURPOSE
|
|
11
11
|
#
|
|
12
12
|
##############################################################################
|
|
13
|
+
import collections.abc
|
|
14
|
+
import typing
|
|
13
15
|
|
|
14
|
-
limited_builtins = {}
|
|
15
16
|
|
|
17
|
+
limited_builtins: dict[str, typing.Any] = {}
|
|
16
18
|
|
|
17
|
-
|
|
19
|
+
|
|
20
|
+
@typing.overload
|
|
21
|
+
def limited_range(iFirst: int) -> collections.abc.Sequence[int]: ...
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@typing.overload
|
|
25
|
+
def limited_range(iStart: int, iEnd: int, /
|
|
26
|
+
) -> collections.abc.Sequence[int]: ...
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@typing.overload
|
|
30
|
+
def limited_range(iStart: int, iEnd: int, iStep: int, /
|
|
31
|
+
) -> collections.abc.Sequence[int]: ...
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def limited_range(iFirst: int, *args: int) -> collections.abc.Sequence[int]:
|
|
18
35
|
# limited range function from Martijn Pieters
|
|
19
36
|
RANGELIMIT = 1000
|
|
20
37
|
if not len(args):
|
|
@@ -41,8 +58,10 @@ def limited_range(iFirst, *args):
|
|
|
41
58
|
|
|
42
59
|
limited_builtins['range'] = limited_range
|
|
43
60
|
|
|
61
|
+
_T = typing.TypeVar('_T')
|
|
62
|
+
|
|
44
63
|
|
|
45
|
-
def limited_list(seq):
|
|
64
|
+
def limited_list(seq: collections.abc.Iterable[_T]) -> list[_T]:
|
|
46
65
|
if isinstance(seq, str):
|
|
47
66
|
raise TypeError('cannot convert string to list')
|
|
48
67
|
return list(seq)
|
|
@@ -51,7 +70,7 @@ def limited_list(seq):
|
|
|
51
70
|
limited_builtins['list'] = limited_list
|
|
52
71
|
|
|
53
72
|
|
|
54
|
-
def limited_tuple(seq):
|
|
73
|
+
def limited_tuple(seq: collections.abc.Iterable[_T]) -> tuple[_T, ...]:
|
|
55
74
|
if isinstance(seq, str):
|
|
56
75
|
raise TypeError('cannot convert string to tuple')
|
|
57
76
|
return tuple(seq)
|
|
@@ -15,17 +15,17 @@
|
|
|
15
15
|
class PrintCollector:
|
|
16
16
|
"""Collect written text, and return it when called."""
|
|
17
17
|
|
|
18
|
-
def __init__(self, _getattr_=None):
|
|
18
|
+
def __init__(self, _getattr_=None): # type: ignore[no-untyped-def]
|
|
19
19
|
self.txt = []
|
|
20
20
|
self._getattr_ = _getattr_
|
|
21
21
|
|
|
22
|
-
def write(self, text):
|
|
22
|
+
def write(self, text: str) -> None:
|
|
23
23
|
self.txt.append(text)
|
|
24
24
|
|
|
25
|
-
def __call__(self):
|
|
25
|
+
def __call__(self) -> str:
|
|
26
26
|
return ''.join(self.txt)
|
|
27
27
|
|
|
28
|
-
def _call_print(self, *objects, **kwargs):
|
|
28
|
+
def _call_print(self, *objects, **kwargs): # type: ignore[no-untyped-def]
|
|
29
29
|
if kwargs.get('file', None) is None:
|
|
30
30
|
kwargs['file'] = self
|
|
31
31
|
else:
|
RestrictedPython/Utilities.py
CHANGED
|
@@ -11,21 +11,24 @@
|
|
|
11
11
|
#
|
|
12
12
|
##############################################################################
|
|
13
13
|
|
|
14
|
+
import collections.abc
|
|
14
15
|
import math
|
|
15
16
|
import random
|
|
16
17
|
import string
|
|
18
|
+
import types
|
|
19
|
+
import typing
|
|
17
20
|
|
|
18
21
|
|
|
19
|
-
utility_builtins = {}
|
|
22
|
+
utility_builtins: dict[str, typing.Any] = {}
|
|
20
23
|
|
|
21
24
|
|
|
22
25
|
class _AttributeDelegator:
|
|
23
|
-
def __init__(self, mod, *excludes):
|
|
26
|
+
def __init__(self, mod: types.ModuleType, *excludes: str):
|
|
24
27
|
"""delegate attribute lookups outside *excludes* to module *mod*."""
|
|
25
28
|
self.__mod = mod
|
|
26
29
|
self.__excludes = excludes
|
|
27
30
|
|
|
28
|
-
def __getattr__(self, attr):
|
|
31
|
+
def __getattr__(self, attr: str) -> typing.Any:
|
|
29
32
|
if attr in self.__excludes:
|
|
30
33
|
raise NotImplementedError(
|
|
31
34
|
f"{self.__mod.__name__}.{attr} is not safe")
|
|
@@ -50,7 +53,7 @@ except ImportError:
|
|
|
50
53
|
pass
|
|
51
54
|
|
|
52
55
|
|
|
53
|
-
def same_type(arg1, *args):
|
|
56
|
+
def same_type(arg1: object, *args: object) -> bool:
|
|
54
57
|
"""Compares the class or type of two or more objects."""
|
|
55
58
|
t = getattr(arg1, '__class__', type(arg1))
|
|
56
59
|
for arg in args:
|
|
@@ -61,8 +64,10 @@ def same_type(arg1, *args):
|
|
|
61
64
|
|
|
62
65
|
utility_builtins['same_type'] = same_type
|
|
63
66
|
|
|
67
|
+
_T = typing.TypeVar('_T')
|
|
64
68
|
|
|
65
|
-
|
|
69
|
+
|
|
70
|
+
def test(*args: _T) -> _T | None:
|
|
66
71
|
length = len(args)
|
|
67
72
|
for i in range(1, length, 2):
|
|
68
73
|
if args[i - 1]:
|
|
@@ -70,12 +75,22 @@ def test(*args):
|
|
|
70
75
|
|
|
71
76
|
if length % 2:
|
|
72
77
|
return args[-1]
|
|
78
|
+
return None
|
|
73
79
|
|
|
74
80
|
|
|
75
81
|
utility_builtins['test'] = test
|
|
76
82
|
|
|
83
|
+
_TK = typing.TypeVar('_TK')
|
|
84
|
+
_TV = typing.TypeVar('_TV')
|
|
85
|
+
_T_in: typing.TypeAlias = collections.abc.Iterable[_TK | tuple[_TK, _TV]]
|
|
86
|
+
_T_out: typing.TypeAlias = list[tuple[_TK, _TK | _TV]]
|
|
87
|
+
|
|
77
88
|
|
|
78
|
-
def reorder(
|
|
89
|
+
def reorder(
|
|
90
|
+
s: _T_in[_TK, _TV],
|
|
91
|
+
with_: collections.abc.Iterable[typing.Any] | None = None,
|
|
92
|
+
without: collections.abc.Iterable[typing.Any] = ()
|
|
93
|
+
) -> _T_out[_TK, _TV]:
|
|
79
94
|
# s, with_, and without are sequences treated as sets.
|
|
80
95
|
# The result is subtract(intersect(s, with_), without),
|
|
81
96
|
# unless with_ is None, in which case it is subtract(s, without).
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
import sys
|
|
3
|
+
import typing
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
_T = typing.TypeVar('_T')
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def cast_not_none(var: _T | None) -> _T:
|
|
10
|
+
return typing.cast(_T, var)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# T_pos_ast are subtypes of ast.AST that have a position
|
|
14
|
+
# (have attributes: lineno, end_lineno, col_offset, and end_col_offset).
|
|
15
|
+
#
|
|
16
|
+
# ast.type_param is a new type in python 3.12 that has a position.
|
|
17
|
+
# TODO: Remove `else` when Support for Python 3.11 is dropped.
|
|
18
|
+
if sys.version_info >= (3, 12):
|
|
19
|
+
T_pos_ast: typing.TypeAlias = (
|
|
20
|
+
ast.stmt | ast.expr | ast.excepthandler | ast.arg | ast.keyword
|
|
21
|
+
| ast.alias | ast.pattern | ast.type_param)
|
|
22
|
+
else:
|
|
23
|
+
T_pos_ast: typing.TypeAlias = (
|
|
24
|
+
ast.stmt | ast.expr | ast.excepthandler | ast.arg | ast.keyword
|
|
25
|
+
| ast.alias | ast.pattern)
|
RestrictedPython/compile.py
CHANGED
|
@@ -1,55 +1,84 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import ast
|
|
4
|
+
import collections.abc
|
|
5
|
+
import os
|
|
6
|
+
import types
|
|
7
|
+
import typing
|
|
2
8
|
import warnings
|
|
3
|
-
from collections import namedtuple
|
|
4
9
|
|
|
5
10
|
from RestrictedPython._compat import IS_CPYTHON
|
|
11
|
+
from RestrictedPython._types import cast_not_none
|
|
6
12
|
from RestrictedPython.transformer import RestrictingNodeTransformer
|
|
13
|
+
from RestrictedPython.transformer import copy_locations
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# Temporary workaround for missing _typeshed
|
|
17
|
+
ReadableBuffer: typing.TypeAlias = bytes | bytearray
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class CompileResult(typing.NamedTuple):
|
|
21
|
+
code: types.CodeType | None
|
|
22
|
+
errors: collections.abc.Sequence[str]
|
|
23
|
+
warnings: collections.abc.Sequence[str]
|
|
24
|
+
used_names: collections.abc.Mapping[str, bool]
|
|
7
25
|
|
|
8
26
|
|
|
9
|
-
CompileResult = namedtuple(
|
|
10
|
-
'CompileResult', 'code, errors, warnings, used_names')
|
|
11
27
|
syntax_error_template = (
|
|
12
|
-
'Line {lineno}: {type}: {msg} at statement: {statement!r}'
|
|
28
|
+
'Line {lineno}: {type}: {msg} at statement: {statement!r}'
|
|
29
|
+
)
|
|
13
30
|
|
|
14
31
|
NOT_CPYTHON_WARNING = (
|
|
15
32
|
'RestrictedPython is only supported on CPython: use on other Python '
|
|
16
33
|
'implementations may create security issues.'
|
|
17
34
|
)
|
|
18
35
|
|
|
36
|
+
_T_ast_compilable: typing.TypeAlias = (
|
|
37
|
+
ast.Module | ast.Expression | ast.Interactive)
|
|
38
|
+
_T_source: typing.TypeAlias = str | ReadableBuffer | _T_ast_compilable
|
|
39
|
+
|
|
19
40
|
|
|
20
41
|
def _compile_restricted_mode(
|
|
21
|
-
source,
|
|
22
|
-
filename='<string>',
|
|
23
|
-
mode="exec",
|
|
24
|
-
flags=0,
|
|
25
|
-
dont_inherit=False,
|
|
26
|
-
policy=RestrictingNodeTransformer
|
|
42
|
+
source: _T_source,
|
|
43
|
+
filename: str | bytes | os.PathLike[typing.Any] = '<string>',
|
|
44
|
+
mode: typing.Literal["exec", "eval", "single"] = "exec",
|
|
45
|
+
flags: int = 0,
|
|
46
|
+
dont_inherit: bool = False,
|
|
47
|
+
policy: type[ast.NodeTransformer] | None = RestrictingNodeTransformer,
|
|
48
|
+
) -> CompileResult:
|
|
27
49
|
|
|
28
50
|
if not IS_CPYTHON:
|
|
29
51
|
warnings.warn_explicit(
|
|
30
52
|
NOT_CPYTHON_WARNING, RuntimeWarning, 'RestrictedPython', 0)
|
|
31
53
|
|
|
32
54
|
byte_code = None
|
|
33
|
-
collected_errors = []
|
|
34
|
-
collected_warnings = []
|
|
35
|
-
used_names = {}
|
|
55
|
+
collected_errors: list[str] = []
|
|
56
|
+
collected_warnings: list[str] = []
|
|
57
|
+
used_names: dict[str, bool] = {}
|
|
36
58
|
if policy is None:
|
|
37
59
|
# Unrestricted Source Checks
|
|
38
60
|
byte_code = compile(source, filename, mode=mode, flags=flags,
|
|
39
61
|
dont_inherit=dont_inherit)
|
|
40
62
|
elif issubclass(policy, RestrictingNodeTransformer):
|
|
41
|
-
|
|
42
|
-
|
|
63
|
+
allowed_source_types = [
|
|
64
|
+
str,
|
|
65
|
+
bytes,
|
|
66
|
+
bytearray,
|
|
67
|
+
ast.Module,
|
|
68
|
+
ast.Expression,
|
|
69
|
+
ast.Interactive]
|
|
43
70
|
if not issubclass(type(source), tuple(allowed_source_types)):
|
|
44
71
|
raise TypeError('Not allowed source type: '
|
|
45
72
|
'"{0.__class__.__name__}".'.format(source))
|
|
46
|
-
c_ast = None
|
|
73
|
+
c_ast: _T_ast_compilable | None = None
|
|
47
74
|
# workaround for pypy issue https://bitbucket.org/pypy/pypy/issues/2552
|
|
48
|
-
if isinstance(source, ast.Module):
|
|
75
|
+
if isinstance(source, (ast.Module, ast.Expression, ast.Interactive)):
|
|
49
76
|
c_ast = source
|
|
50
77
|
else:
|
|
51
78
|
try:
|
|
52
|
-
c_ast =
|
|
79
|
+
c_ast = typing.cast(
|
|
80
|
+
_T_ast_compilable, ast.parse(
|
|
81
|
+
source, filename, mode))
|
|
53
82
|
except (TypeError, ValueError) as e:
|
|
54
83
|
collected_errors.append(str(e))
|
|
55
84
|
except SyntaxError as v:
|
|
@@ -78,11 +107,12 @@ def _compile_restricted_mode(
|
|
|
78
107
|
|
|
79
108
|
|
|
80
109
|
def compile_restricted_exec(
|
|
81
|
-
source,
|
|
82
|
-
filename='<string>',
|
|
83
|
-
flags=0,
|
|
84
|
-
dont_inherit=False,
|
|
85
|
-
policy=RestrictingNodeTransformer
|
|
110
|
+
source: _T_source,
|
|
111
|
+
filename: str | bytes | os.PathLike[typing.Any] = '<string>',
|
|
112
|
+
flags: int = 0,
|
|
113
|
+
dont_inherit: bool = False,
|
|
114
|
+
policy: type[ast.NodeTransformer] | None = RestrictingNodeTransformer,
|
|
115
|
+
) -> CompileResult:
|
|
86
116
|
"""Compile restricted for the mode `exec`."""
|
|
87
117
|
return _compile_restricted_mode(
|
|
88
118
|
source,
|
|
@@ -94,11 +124,12 @@ def compile_restricted_exec(
|
|
|
94
124
|
|
|
95
125
|
|
|
96
126
|
def compile_restricted_eval(
|
|
97
|
-
source,
|
|
98
|
-
filename='<string>',
|
|
99
|
-
flags=0,
|
|
100
|
-
dont_inherit=False,
|
|
101
|
-
policy=RestrictingNodeTransformer
|
|
127
|
+
source: _T_source,
|
|
128
|
+
filename: str | bytes | os.PathLike[typing.Any] = '<string>',
|
|
129
|
+
flags: int = 0,
|
|
130
|
+
dont_inherit: bool = False,
|
|
131
|
+
policy: type[ast.NodeTransformer] | None = RestrictingNodeTransformer,
|
|
132
|
+
) -> CompileResult:
|
|
102
133
|
"""Compile restricted for the mode `eval`."""
|
|
103
134
|
return _compile_restricted_mode(
|
|
104
135
|
source,
|
|
@@ -110,11 +141,12 @@ def compile_restricted_eval(
|
|
|
110
141
|
|
|
111
142
|
|
|
112
143
|
def compile_restricted_single(
|
|
113
|
-
source,
|
|
114
|
-
filename='<string>',
|
|
115
|
-
flags=0,
|
|
116
|
-
dont_inherit=False,
|
|
117
|
-
policy=RestrictingNodeTransformer
|
|
144
|
+
source: _T_source,
|
|
145
|
+
filename: str | bytes | os.PathLike[typing.Any] = '<string>',
|
|
146
|
+
flags: int = 0,
|
|
147
|
+
dont_inherit: bool = False,
|
|
148
|
+
policy: type[ast.NodeTransformer] | None = RestrictingNodeTransformer,
|
|
149
|
+
) -> CompileResult:
|
|
118
150
|
"""Compile restricted for the mode `single`."""
|
|
119
151
|
return _compile_restricted_mode(
|
|
120
152
|
source,
|
|
@@ -126,30 +158,40 @@ def compile_restricted_single(
|
|
|
126
158
|
|
|
127
159
|
|
|
128
160
|
def compile_restricted_function(
|
|
129
|
-
p, # parameters
|
|
130
|
-
body,
|
|
131
|
-
name,
|
|
132
|
-
filename='<string>',
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
161
|
+
p: str, # parameters
|
|
162
|
+
body: _T_source,
|
|
163
|
+
name: str,
|
|
164
|
+
filename: str | bytes | os.PathLike[typing.Any] = '<string>',
|
|
165
|
+
# List of globals (e.g. ['here', 'context', ...])
|
|
166
|
+
globalize: list[str] | None = None,
|
|
167
|
+
flags: int = 0,
|
|
168
|
+
dont_inherit: bool = False,
|
|
169
|
+
policy: type[ast.NodeTransformer] | None = RestrictingNodeTransformer,
|
|
170
|
+
) -> CompileResult:
|
|
137
171
|
"""Compile a restricted code object for a function.
|
|
138
172
|
|
|
139
173
|
Documentation see:
|
|
140
174
|
http://restrictedpython.readthedocs.io/en/latest/usage/index.html#RestrictedPython.compile_restricted_function
|
|
141
175
|
"""
|
|
142
176
|
# Parse the parameters and body, then combine them.
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
177
|
+
body_ast: list[ast.stmt]
|
|
178
|
+
if isinstance(body, ast.Expression):
|
|
179
|
+
_body_ast = ast.Expr(body.body)
|
|
180
|
+
copy_locations(_body_ast, body.body)
|
|
181
|
+
body_ast = [_body_ast]
|
|
182
|
+
elif isinstance(body, (ast.Module, ast.Interactive)):
|
|
183
|
+
body_ast = body.body
|
|
184
|
+
else:
|
|
185
|
+
try:
|
|
186
|
+
body_ast = ast.parse(body, '<func code>', 'exec').body
|
|
187
|
+
except SyntaxError as v:
|
|
188
|
+
error = syntax_error_template.format(
|
|
189
|
+
lineno=v.lineno,
|
|
190
|
+
type=v.__class__.__name__,
|
|
191
|
+
msg=v.msg,
|
|
192
|
+
statement=v.text.strip() if v.text else None)
|
|
193
|
+
return CompileResult(
|
|
194
|
+
code=None, errors=(error,), warnings=(), used_names={})
|
|
153
195
|
|
|
154
196
|
# The compiled code is actually executed inside a function
|
|
155
197
|
# (that is called when the code is called) so reading and assigning to a
|
|
@@ -157,7 +199,7 @@ def compile_restricted_function(
|
|
|
157
199
|
# UnboundLocalError.
|
|
158
200
|
# We don't want the user to need to understand this.
|
|
159
201
|
if globalize:
|
|
160
|
-
body_ast.
|
|
202
|
+
body_ast.insert(0, ast.Global(globalize))
|
|
161
203
|
wrapper_ast = ast.parse('def masked_function_name(%s): pass' % p,
|
|
162
204
|
'<func wrapper>', 'exec')
|
|
163
205
|
# In case the name you chose for your generated function is not a
|
|
@@ -166,7 +208,7 @@ def compile_restricted_function(
|
|
|
166
208
|
assert isinstance(function_ast, ast.FunctionDef)
|
|
167
209
|
function_ast.name = name
|
|
168
210
|
|
|
169
|
-
|
|
211
|
+
function_ast.body = body_ast
|
|
170
212
|
wrapper_ast = ast.fix_missing_locations(wrapper_ast)
|
|
171
213
|
|
|
172
214
|
result = _compile_restricted_mode(
|
|
@@ -181,18 +223,19 @@ def compile_restricted_function(
|
|
|
181
223
|
|
|
182
224
|
|
|
183
225
|
def compile_restricted(
|
|
184
|
-
source,
|
|
185
|
-
filename='<unknown>',
|
|
186
|
-
mode='exec',
|
|
187
|
-
flags=0,
|
|
188
|
-
dont_inherit=False,
|
|
189
|
-
policy=RestrictingNodeTransformer
|
|
226
|
+
source: _T_source,
|
|
227
|
+
filename: str | bytes | os.PathLike[typing.Any] = '<unknown>',
|
|
228
|
+
mode: typing.Literal["exec", "eval", "single"] = 'exec',
|
|
229
|
+
flags: int = 0,
|
|
230
|
+
dont_inherit: bool = False,
|
|
231
|
+
policy: type[ast.NodeTransformer] | None = RestrictingNodeTransformer,
|
|
232
|
+
) -> types.CodeType:
|
|
190
233
|
"""Replacement for the built-in compile() function.
|
|
191
234
|
|
|
192
235
|
policy ... `ast.NodeTransformer` class defining the restrictions.
|
|
193
236
|
|
|
194
237
|
"""
|
|
195
|
-
if mode in ['exec', 'eval', 'single'
|
|
238
|
+
if mode in ['exec', 'eval', 'single']:
|
|
196
239
|
result = _compile_restricted_mode(
|
|
197
240
|
source,
|
|
198
241
|
filename=filename,
|
|
@@ -209,4 +252,4 @@ def compile_restricted(
|
|
|
209
252
|
)
|
|
210
253
|
if result.errors:
|
|
211
254
|
raise SyntaxError(result.errors)
|
|
212
|
-
return result.code
|
|
255
|
+
return cast_not_none(result.code)
|
|
File without changes
|