• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

zopefoundation / RestrictedPython / 11255649889

09 Oct 2024 01:08PM UTC coverage: 98.774% (-0.1%) from 98.869%
11255649889

Pull #289

github

dataflake
Merge branch 'master' into py313
Pull Request #289: Support Python 3.13 and remove Python 3.7 support.

214 of 233 branches covered (91.85%)

16 of 17 new or added lines in 3 files covered. (94.12%)

27 existing lines in 4 files now uncovered.

2498 of 2529 relevant lines covered (98.77%)

0.99 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.55
/src/RestrictedPython/compile.py
1
import ast
1✔
2
import warnings
1✔
3
from collections import namedtuple
1✔
4

5
from RestrictedPython._compat import IS_CPYTHON
1✔
6
from RestrictedPython.transformer import RestrictingNodeTransformer
1✔
7

8

9
CompileResult = namedtuple(
1✔
10
    'CompileResult', 'code, errors, warnings, used_names')
11
syntax_error_template = (
1✔
12
    'Line {lineno}: {type}: {msg} at statement: {statement!r}')
13

14
NOT_CPYTHON_WARNING = (
1✔
15
    'RestrictedPython is only supported on CPython: use on other Python '
16
    'implementations may create security issues.'
17
)
18

19

20
def _compile_restricted_mode(
1✔
21
        source,
22
        filename='<string>',
23
        mode="exec",
24
        flags=0,
25
        dont_inherit=False,
26
        policy=RestrictingNodeTransformer):
27

28
    if not IS_CPYTHON:
1✔
29
        warnings.warn_explicit(
1✔
30
            NOT_CPYTHON_WARNING, RuntimeWarning, 'RestrictedPython', 0)
31

32
    byte_code = None
1✔
33
    collected_errors = []
1✔
34
    collected_warnings = []
1✔
35
    used_names = {}
1✔
36
    if policy is None:
1✔
37
        # Unrestricted Source Checks
38
        byte_code = compile(source, filename, mode=mode, flags=flags,
1✔
39
                            dont_inherit=dont_inherit)
40
    elif issubclass(policy, RestrictingNodeTransformer):
1✔
41
        c_ast = None
1✔
42
        allowed_source_types = [str, ast.Module]
1✔
43
        if not issubclass(type(source), tuple(allowed_source_types)):
1✔
44
            raise TypeError('Not allowed source type: '
1✔
45
                            '"{0.__class__.__name__}".'.format(source))
46
        c_ast = None
1✔
47
        # workaround for pypy issue https://bitbucket.org/pypy/pypy/issues/2552
48
        if isinstance(source, ast.Module):
1✔
49
            c_ast = source
1✔
50
        else:
51
            try:
1✔
52
                c_ast = ast.parse(source, filename, mode)
1✔
53
            except (TypeError, ValueError) as e:
1✔
UNCOV
54
                collected_errors.append(str(e))
×
55
            except SyntaxError as v:
1✔
56
                collected_errors.append(syntax_error_template.format(
1✔
57
                    lineno=v.lineno,
58
                    type=v.__class__.__name__,
59
                    msg=v.msg,
60
                    statement=v.text.strip() if v.text else None
61
                ))
62
        if c_ast:
1✔
63
            policy_instance = policy(
1✔
64
                collected_errors, collected_warnings, used_names)
65
            policy_instance.visit(c_ast)
1✔
66
            if not collected_errors:
1✔
67
                byte_code = compile(c_ast, filename, mode=mode  # ,
1✔
68
                                    # flags=flags,
69
                                    # dont_inherit=dont_inherit
70
                                    )
71
    else:
72
        raise TypeError('Unallowed policy provided for RestrictedPython')
1✔
73
    return CompileResult(
1✔
74
        byte_code,
75
        tuple(collected_errors),
76
        collected_warnings,
77
        used_names)
78

79

80
def compile_restricted_exec(
1✔
81
        source,
82
        filename='<string>',
83
        flags=0,
84
        dont_inherit=False,
85
        policy=RestrictingNodeTransformer):
86
    """Compile restricted for the mode `exec`."""
87
    return _compile_restricted_mode(
1✔
88
        source,
89
        filename=filename,
90
        mode='exec',
91
        flags=flags,
92
        dont_inherit=dont_inherit,
93
        policy=policy)
94

95

96
def compile_restricted_eval(
1✔
97
        source,
98
        filename='<string>',
99
        flags=0,
100
        dont_inherit=False,
101
        policy=RestrictingNodeTransformer):
102
    """Compile restricted for the mode `eval`."""
103
    return _compile_restricted_mode(
1✔
104
        source,
105
        filename=filename,
106
        mode='eval',
107
        flags=flags,
108
        dont_inherit=dont_inherit,
109
        policy=policy)
110

111

112
def compile_restricted_single(
1✔
113
        source,
114
        filename='<string>',
115
        flags=0,
116
        dont_inherit=False,
117
        policy=RestrictingNodeTransformer):
118
    """Compile restricted for the mode `single`."""
119
    return _compile_restricted_mode(
1✔
120
        source,
121
        filename=filename,
122
        mode='single',
123
        flags=flags,
124
        dont_inherit=dont_inherit,
125
        policy=policy)
126

127

128
def compile_restricted_function(
1✔
129
        p,  # parameters
130
        body,
131
        name,
132
        filename='<string>',
133
        globalize=None,  # List of globals (e.g. ['here', 'context', ...])
134
        flags=0,
135
        dont_inherit=False,
136
        policy=RestrictingNodeTransformer):
137
    """Compile a restricted code object for a function.
138

139
    Documentation see:
140
    http://restrictedpython.readthedocs.io/en/latest/usage/index.html#RestrictedPython.compile_restricted_function
141
    """
142
    # Parse the parameters and body, then combine them.
143
    try:
1✔
144
        body_ast = ast.parse(body, '<func code>', 'exec')
1✔
145
    except SyntaxError as v:
1✔
146
        error = syntax_error_template.format(
1✔
147
            lineno=v.lineno,
148
            type=v.__class__.__name__,
149
            msg=v.msg,
150
            statement=v.text.strip() if v.text else None)
151
        return CompileResult(
1✔
152
            code=None, errors=(error,), warnings=(), used_names=())
153

154
    # The compiled code is actually executed inside a function
155
    # (that is called when the code is called) so reading and assigning to a
156
    # global variable like this`printed += 'foo'` would throw an
157
    # UnboundLocalError.
158
    # We don't want the user to need to understand this.
159
    if globalize:
1✔
160
        body_ast.body.insert(0, ast.Global(globalize))
1✔
161
    wrapper_ast = ast.parse('def masked_function_name(%s): pass' % p,
1✔
162
                            '<func wrapper>', 'exec')
163
    # In case the name you chose for your generated function is not a
164
    # valid python identifier we set it after the fact
165
    function_ast = wrapper_ast.body[0]
1✔
166
    assert isinstance(function_ast, ast.FunctionDef)
1✔
167
    function_ast.name = name
1✔
168

169
    wrapper_ast.body[0].body = body_ast.body
1✔
170
    wrapper_ast = ast.fix_missing_locations(wrapper_ast)
1✔
171

172
    result = _compile_restricted_mode(
1✔
173
        wrapper_ast,
174
        filename=filename,
175
        mode='exec',
176
        flags=flags,
177
        dont_inherit=dont_inherit,
178
        policy=policy)
179

180
    return result
1✔
181

182

183
def compile_restricted(
1✔
184
        source,
185
        filename='<unknown>',
186
        mode='exec',
187
        flags=0,
188
        dont_inherit=False,
189
        policy=RestrictingNodeTransformer):
190
    """Replacement for the built-in compile() function.
191

192
    policy ... `ast.NodeTransformer` class defining the restrictions.
193

194
    """
195
    if mode in ['exec', 'eval', 'single', 'function']:
1✔
196
        result = _compile_restricted_mode(
1✔
197
            source,
198
            filename=filename,
199
            mode=mode,
200
            flags=flags,
201
            dont_inherit=dont_inherit,
202
            policy=policy)
203
    else:
204
        raise TypeError('unknown mode %s', mode)
1✔
205
    for warning in result.warnings:
1✔
206
        warnings.warn(
1✔
207
            warning,
208
            SyntaxWarning
209
        )
210
    if result.errors:
1✔
211
        raise SyntaxError(result.errors)
1✔
212
    return result.code
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc