File: compiling.py

package info (click to toggle)
pypy3 7.3.11%2Bdfsg-2%2Bdeb12u3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 201,024 kB
  • sloc: python: 1,950,308; ansic: 517,580; sh: 21,417; asm: 14,419; cpp: 4,263; makefile: 4,228; objc: 761; xml: 530; exp: 499; javascript: 314; pascal: 244; lisp: 45; csh: 11; awk: 4
file content (243 lines) | stat: -rw-r--r-- 10,313 bytes parent folder | download
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
241
242
243
"""
Implementation of the interpreter-level compile/eval builtins.
"""

from pypy.interpreter.pycode import PyCode
from pypy.interpreter.error import OperationError, oefmt
from pypy.interpreter.astcompiler import consts, ast
from pypy.interpreter.gateway import unwrap_spec
from pypy.interpreter.argument import Arguments
from pypy.interpreter.nestedscope import Cell
from pypy.interpreter.function import Function

@unwrap_spec(filename='fsencode', mode='text', flags=int, dont_inherit=int,
             optimize=int, _feature_version=int)
def compile(space, w_source, filename, mode, flags=0, dont_inherit=0,
            optimize=-1, _feature_version=-1):
    """Compile the source string (a Python module, statement or expression)
into a code object that can be executed by the exec statement or eval().
The filename will be used for run-time error messages.
The mode must be 'exec' to compile a module, 'single' to compile a
single (interactive) statement, or 'eval' to compile an expression.
The flags argument, if present, controls which future statements influence
the compilation of the code.
The dont_inherit argument, if non-zero, stops the compilation inheriting
the effects of any future statements in effect in the code calling
compile; if absent or zero these statements do influence the compilation,
in addition to any features explicitly specified.
"""
    from pypy.interpreter.pyopcode import source_as_str
    # only allow default value of _feature_version for now
    # we need to support the keyword argument, the ast module passes it (set to
    # -1, usually)
    if _feature_version >= 0 and (flags & consts.PyCF_ONLY_AST):
        feature_version = _feature_version
    else:
        feature_version = -1

    ec = space.getexecutioncontext()
    if flags & ~(ec.compiler.compiler_flags | consts.PyCF_ONLY_AST |
                 consts.PyCF_DONT_IMPLY_DEDENT | consts.PyCF_SOURCE_IS_UTF8 |
                 consts.PyCF_ACCEPT_NULL_BYTES | consts.PyCF_TYPE_COMMENTS |
                 consts.PyCF_ALLOW_TOP_LEVEL_AWAIT):
        raise oefmt(space.w_ValueError, "compile() unrecognized flags")

    only_ast = flags & consts.PyCF_ONLY_AST

    if not dont_inherit:
        caller = ec.gettopframe_nohidden()
        if caller:
            flags |= ec.compiler.getcodeflags(caller.getcode())

    if mode not in ('exec', 'eval', 'single', 'func_type'):
        if only_ast:
            raise oefmt(space.w_ValueError,
                        "compile() mode must be 'exec', 'eval', 'single' or 'func_type'")
        else:
            raise oefmt(space.w_ValueError,
                        "compile() arg 3 must be 'exec', 'eval' or 'single'")

    if mode == "func_type" and not only_ast:
        raise oefmt(space.w_ValueError,
                    "compile() mode 'func_type' requires flag PyCF_ONLY_AST")

    if optimize < -1 or optimize > 2:
        raise oefmt(space.w_ValueError,
            "compile(): invalid optimize value")

    if space.isinstance_w(w_source, space.gettypeobject(ast.W_AST.typedef)):
        if only_ast:
            return w_source
        ast_node = ast.mod.from_object(space, w_source)
        ec.compiler.validate_ast(ast_node)
        return ec.compiler.compile_ast(ast_node, filename, mode, flags,
                                       optimize=optimize)

    flags |= consts.PyCF_SOURCE_IS_UTF8
    source, flags = source_as_str(space, w_source, 'compile',
                                  "string, bytes or AST", flags)

    if only_ast:
        node = ec.compiler.compile_to_ast(source, filename, mode, flags, feature_version)
        return node.to_object(space)
    else:
        return ec.compiler.compile(source, filename, mode, flags,
                                   optimize=optimize)


def eval(space, w_prog, w_globals=None, w_locals=None):
    """Evaluate the source in the context of globals and locals.
The source may be a string representing a Python expression
or a code object as returned by compile().  The globals and locals
are dictionaries, defaulting to the current current globals and locals.
If only globals is given, locals defaults to it.
"""
    from pypy.interpreter.pyopcode import ensure_ns, source_as_str
    w_globals, w_locals = ensure_ns(space, w_globals, w_locals, 'eval')

    if space.isinstance_w(w_prog, space.gettypeobject(PyCode.typedef)):
        code = space.interp_w(PyCode, w_prog)
        space.audit("exec", [w_prog])
    else:
        source, flags = source_as_str(space, w_prog, 'eval',
                                      "string, bytes or code",
                                      consts.PyCF_SOURCE_IS_UTF8)
        ec = space.getexecutioncontext()
        code = ec.compiler.compile(source.lstrip(' \t'), "<string>", 'eval',
                                   flags)
    if space.finditem_str(w_globals, "__builtins__") is None:
        space.setitem_str(w_globals, "__builtins__", space.builtin)

    return code.exec_code(space, w_globals, w_locals)

def exec_(space, w_prog, w_globals=None, w_locals=None):
    """
    exec(source, globals=None, locals=None, /)

    Execute the given source in the context of globals and locals.

    The source may be a string representing one or more Python statements
    or a code object as returned by compile().
    The globals must be a dictionary and locals can be any mapping,
    defaulting to the current globals and locals.
    If only globals is given, locals defaults to it.
    """

    frame = space.getexecutioncontext().gettopframe()
    frame.exec_(w_prog, w_globals, w_locals)

def _update_bases(space, w_bases, bases_w):
    new_bases_w = None
    for i, w_base in enumerate(bases_w):
        if space.isinstance_w(w_base, space.w_type):
            if new_bases_w is not None:
                new_bases_w.append(w_base)
            continue
        w_meth = None
        try:
            # one of the few places where cpython uses getattr not lookup
            w_meth = space.getattr(w_base, space.newtext('__mro_entries__'))
        except OperationError as e:
            if not e.match(space, space.w_AttributeError):
                raise
            if new_bases_w is not None:
                new_bases_w.append(w_base)
        else:
            w_new_base = space.call_function(w_meth, w_bases)
            if not space.isinstance_w(w_new_base, space.w_tuple):
                raise oefmt(space.w_TypeError, "__mro_entries__ must return a tuple")
            if new_bases_w is None:
                new_bases_w = bases_w[:i]
            new_bases_w.extend(space.fixedview(w_new_base))
    if new_bases_w is None:
        return bases_w
    return new_bases_w[:]

def build_class(space, w_func, w_name, __args__):
    from pypy.objspace.std.typeobject import _calculate_metaclass, W_TypeObject
    from pypy.interpreter.nestedscope import Cell
    if not isinstance(w_func, Function):
        raise oefmt(space.w_TypeError, "__build_class__: func must be a function")
    orig_bases_w, kwds_w = __args__.unpack()
    w_orig_bases = space.newtuple(orig_bases_w)
    bases_w = _update_bases(space, w_orig_bases, orig_bases_w)
    w_bases = space.newtuple(bases_w)
    w_meta = kwds_w.pop('metaclass', None)
    if w_meta is not None:
        isclass = space.isinstance_w(w_meta, space.w_type)
    else:
        if bases_w:
            w_meta = space.type(bases_w[0])
        else:
            w_meta = space.w_type
        isclass = True
    if isclass:
        # w_meta is really a class, so check for a more derived
        # metaclass, or possible metaclass conflicts
        w_meta = _calculate_metaclass(space, w_meta, bases_w)

    try:
        w_prep = space.getattr(w_meta, space.newtext("__prepare__"))
    except OperationError as e:
        if not e.match(space, space.w_AttributeError):
            raise
        w_namespace = space.newdict(module=True)
    else:
        # XXX mess
        keyword_names_w = [space.newtext(kwd) for kwd in kwds_w.keys()]
        args = Arguments(space,
                         args_w=[w_name, w_bases],
                         keyword_names_w=keyword_names_w,
                         keywords_w=kwds_w.values())
        w_namespace = space.call_args(w_prep, args)
    if not space.ismapping_w(w_namespace):
        if isclass:
            raise oefmt(space.w_TypeError,
                "%N.__prepare__() must return a mapping, not %T",
                w_meta, w_namespace)
        else:
            raise oefmt(space.w_TypeError,
                "<metaclass>.__prepare__() must return a mapping, not %T",
                w_namespace)

    code = w_func.getcode()
    frame = space.createframe(code, w_func.w_func_globals, w_func)
    frame.setdictscope(w_namespace)
    w_cell = frame.run()
    if bases_w is not orig_bases_w:
        space.setitem(w_namespace, space.newtext("__orig_bases__"), w_orig_bases)
    keyword_names_w = [space.newtext(kwd) for kwd in kwds_w.keys()]
    args = Arguments(space,
                     args_w=[w_name, w_bases, w_namespace],
                     keyword_names_w=keyword_names_w,
                     keywords_w=kwds_w.values())
    try:
        w_class = space.call_args(w_meta, args)
    except OperationError as e:
        # give a more comprehensible error message for TypeErrors
        if e.got_any_traceback():
            raise
        if (not e.match(space, space.w_TypeError) or
                space.is_w(w_meta, space.w_type)):
            raise
        raise oefmt(space.w_TypeError,
            "metaclass found to be '%N', but calling %R "
            "with args (%R, %R, ...) raised %R",
            w_meta, w_meta, w_name, w_bases,
            e.get_w_value(space))
    if isinstance(w_cell, Cell) and isinstance(w_class, W_TypeObject):
        if w_cell.empty():
            raise oefmt(space.w_RuntimeError,
                "__class__ not set defining %S as %S. "
                "Was __classcell__ propagated to type.__new__?",
                    w_name,
                    w_class)

        else:
            w_class_from_cell = w_cell.get()
            if not space.is_w(w_class, w_class_from_cell):
                raise oefmt(
                        space.w_TypeError,
                        "__class__ set to %S defining %S as %S",
                        w_class_from_cell, w_name, w_class)
    return w_class