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 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343
|
from pypy.interpreter.error import oefmt, OperationError
from pypy.interpreter.astcompiler import consts
from rpython.rtyper.lltypesystem import rffi, lltype
from rpython.rlib.objectmodel import we_are_translated
from rpython.rlib.rarithmetic import widen
from pypy.module.cpyext.api import (
cpython_api, CANNOT_FAIL, CONST_STRING, FILEP, fread, feof, Py_ssize_tP,
cpython_struct, ferror)
from pypy.module.cpyext.pyobject import PyObject
from pypy.module.cpyext.pyerrors import PyErr_SetFromErrno
from pypy.module.cpyext.frameobject import PyFrameObject
from pypy.module.__builtin__ import compiling
PyCompilerFlags = cpython_struct(
"PyCompilerFlags", (("cf_flags", rffi.INT),
("cf_feature_version", rffi.INT)))
PyCompilerFlagsPtr = lltype.Ptr(PyCompilerFlags)
PyCF_MASK = (consts.CO_FUTURE_DIVISION |
consts.CO_FUTURE_ABSOLUTE_IMPORT |
consts.CO_FUTURE_WITH_STATEMENT |
consts.CO_FUTURE_PRINT_FUNCTION |
consts.CO_FUTURE_UNICODE_LITERALS)
@cpython_api([PyObject, PyObject, PyObject], PyObject)
def PyEval_CallObjectWithKeywords(space, w_obj, w_arg, w_kwds):
return space.call(w_obj, w_arg, w_kwds)
@cpython_api([], PyObject, result_borrowed=True)
def PyEval_GetBuiltins(space):
"""Return a dictionary of the builtins in the current execution
frame, or the interpreter of the thread state if no frame is
currently executing."""
caller = space.getexecutioncontext().gettopframe_nohidden()
if caller is not None:
w_globals = caller.get_w_globals()
w_builtins = space.getitem(w_globals, space.newtext('__builtins__'))
if not space.isinstance_w(w_builtins, space.w_dict):
w_builtins = w_builtins.getdict(space)
else:
w_builtins = space.builtin.getdict(space)
return w_builtins # borrowed ref in all cases
@cpython_api([], PyObject, error=CANNOT_FAIL, result_borrowed=True)
def PyEval_GetLocals(space):
"""Return a dictionary of the local variables in the current execution
frame, or NULL if no frame is currently executing."""
caller = space.getexecutioncontext().gettopframe_nohidden()
if caller is None:
return None
return caller.getdictscope() # borrowed ref
@cpython_api([], PyObject, error=CANNOT_FAIL, result_borrowed=True)
def PyEval_GetGlobals(space):
"""Return a dictionary of the global variables in the current execution
frame, or NULL if no frame is currently executing."""
caller = space.getexecutioncontext().gettopframe_nohidden()
if caller is None:
return None
return caller.get_w_globals() # borrowed ref
@cpython_api([], PyFrameObject, error=CANNOT_FAIL, result_borrowed=True)
def PyEval_GetFrame(space):
caller = space.getexecutioncontext().gettopframe_nohidden()
return caller # borrowed ref, may be null
@cpython_api([PyObject, PyObject, PyObject], PyObject)
def PyEval_EvalCode(space, w_code, w_globals, w_locals):
"""This is a simplified interface to PyEval_EvalCodeEx(), with just
the code object, and the dictionaries of global and local variables.
The other arguments are set to NULL."""
if w_globals is None:
w_globals = space.w_None
if w_locals is None:
w_locals = space.w_None
return compiling.eval(space, w_code, w_globals, w_locals)
@cpython_api([PyObject, PyObject], PyObject)
def PyObject_CallObject(space, w_obj, w_arg):
"""
Call a callable Python object callable_object, with arguments given by the
tuple args. If no arguments are needed, then args may be NULL. Returns
the result of the call on success, or NULL on failure. This is the equivalent
of the Python expression apply(callable_object, args) or
callable_object(*args)."""
return space.call(w_obj, w_arg)
@cpython_api([PyObject], PyObject)
def PyObject_CallNoArgs(space, w_obj):
return space.call_function(w_obj)
@cpython_api([PyObject, PyObject], PyObject)
def PyObject_CallOneArg(space, w_obj, w_arg):
return space.call_function(w_obj, w_arg)
@cpython_api([PyObject, PyObject], PyObject)
def PyObject_CallMethodNoArgs(space, w_self, w_name):
name = space.text_w(w_name)
return space.call_method(w_self, name)
@cpython_api([PyObject, PyObject, PyObject], PyObject)
def PyObject_CallMethodOneArg(space, w_self, w_name, w_arg):
name = space.text_w(w_name)
return space.call_method(w_self, name, w_arg)
@cpython_api([PyObject, PyObject, PyObject], PyObject)
def PyObject_Call(space, w_obj, w_args, w_kw):
"""
Call a callable Python object, with arguments given by the
tuple args, and named arguments given by the dictionary kw. If no named
arguments are needed, kw may be NULL. args must not be NULL, use an
empty tuple if no arguments are needed. Returns the result of the call on
success, or NULL on failure. This is the equivalent of the Python expression
apply(callable_object, args, kw) or callable_object(*args, **kw)."""
return space.call(w_obj, w_args, w_kw)
@cpython_api([PyObject, PyObject, PyObject], PyObject)
def PyCFunction_Call(space, w_obj, w_args, w_kw):
return space.call(w_obj, w_args, w_kw)
# These constants are also defined in include/eval.h
Py_single_input = 256
Py_file_input = 257
Py_eval_input = 258
Py_func_type_input = 345
def compile_string(space, source, filename, start, flags=0, feature_version=-1):
w_source = space.newbytes(source)
start = rffi.cast(lltype.Signed, start)
if start == Py_file_input:
mode = 'exec'
elif start == Py_eval_input:
mode = 'eval'
elif start == Py_single_input:
mode = 'single'
elif start == Py_func_type_input:
mode = 'func_type'
else:
raise oefmt(space.w_ValueError,
"invalid mode parameter for compilation")
return compiling.compile(space, w_source, filename, mode, flags,
_feature_version=feature_version)
def run_string(space, source, filename, start, w_globals, w_locals):
w_code = compile_string(space, source, filename, start)
return compiling.eval(space, w_code, w_globals, w_locals)
@cpython_api([CONST_STRING], rffi.INT_real, error=-1)
def PyRun_SimpleString(space, command):
"""This is a simplified interface to PyRun_SimpleStringFlags() below,
leaving the PyCompilerFlags* argument set to NULL."""
command = rffi.charp2str(command)
run_string(space, command, "<string>", Py_file_input,
space.w_None, space.w_None)
return 0
@cpython_api([CONST_STRING, rffi.INT_real,PyObject, PyObject], PyObject)
def PyRun_String(space, source, start, w_globals, w_locals):
"""This is a simplified interface to PyRun_StringFlags() below, leaving
flags set to NULL."""
source = rffi.charp2str(source)
filename = "<string>"
return run_string(space, source, filename, start, w_globals, w_locals)
@cpython_api([CONST_STRING, rffi.INT_real, PyObject, PyObject,
PyCompilerFlagsPtr], PyObject)
def PyRun_StringFlags(space, source, start, w_globals, w_locals, flagsptr):
"""Execute Python source code from str in the context specified by the
dictionaries globals and locals with the compiler flags specified by
flags. The parameter start specifies the start token that should be used to
parse the source code.
Returns the result of executing the code as a Python object, or NULL if an
exception was raised."""
source = rffi.charp2str(source)
if flagsptr:
flags = rffi.cast(lltype.Signed, flagsptr.c_cf_flags)
feature_version = rffi.cast(lltype.Signed, flagsptr.c_cf_feature_version)
else:
flags = 0
feature_version = -1
w_code = compile_string(space, source, "<string>", start, flags=flags,
feature_version=feature_version)
return compiling.eval(space, w_code, w_globals, w_locals)
@cpython_api([FILEP, CONST_STRING, rffi.INT_real, PyObject, PyObject], PyObject)
def PyRun_File(space, fp, filename, start, w_globals, w_locals):
"""This is a simplified interface to PyRun_FileExFlags() below, leaving
closeit set to 0 and flags set to NULL."""
BUF_SIZE = 8192
source = ""
filename = rffi.charp2str(filename)
with rffi.scoped_alloc_buffer(BUF_SIZE) as buf:
while True:
try:
count = fread(buf.raw, 1, BUF_SIZE, fp)
except OSError:
PyErr_SetFromErrno(space, space.w_IOError)
return
count = rffi.cast(lltype.Signed, count)
source += rffi.charpsize2str(buf.raw, count)
if count < BUF_SIZE:
if ferror(fp):
PyErr_SetFromErrno(space, space.w_IOError)
return
if feof(fp):
break
PyErr_SetFromErrno(space, space.w_IOError)
return run_string(space, source, filename, start, w_globals, w_locals)
# Undocumented function!
@cpython_api([PyObject, Py_ssize_tP], rffi.INT_real, error=0)
def _PyEval_SliceIndex(space, w_obj, pi):
"""Extract a slice index from a PyInt or PyLong or an object with the
nb_index slot defined, and store in *pi.
Silently reduce values larger than PY_SSIZE_T_MAX to PY_SSIZE_T_MAX,
and silently boost values less than -PY_SSIZE_T_MAX-1 to -PY_SSIZE_T_MAX-1.
Return 0 on error, 1 on success.
Note: If v is NULL, return success without storing into *pi. This
is because_PyEval_SliceIndex() is called by apply_slice(), which can be
called by the SLICE opcode with v and/or w equal to NULL.
"""
if w_obj is not None:
pi[0] = space.getindex_w(w_obj, None)
return 1
@cpython_api([CONST_STRING, CONST_STRING, rffi.INT_real, PyCompilerFlagsPtr],
PyObject)
def Py_CompileStringFlags(space, source, filename, start, flagsptr):
"""Parse and compile the Python source code in str, returning the
resulting code object. The start token is given by start; this
can be used to constrain the code which can be compiled and should
be Py_eval_input, Py_file_input, or Py_single_input. The filename
specified by filename is used to construct the code object and may
appear in tracebacks or SyntaxError exception messages. This
returns NULL if the code cannot be parsed or compiled."""
source = rffi.charp2str(source)
filename = rffi.charp2str(filename)
if flagsptr:
flags = rffi.cast(lltype.Signed, flagsptr.c_cf_flags)
else:
flags = 0
return compile_string(space, source, filename, start, flags)
@cpython_api([PyCompilerFlagsPtr], rffi.INT_real, error=CANNOT_FAIL)
def PyEval_MergeCompilerFlags(space, cf):
"""This function changes the flags of the current evaluation
frame, and returns true on success, false on failure."""
flags = rffi.cast(lltype.Signed, cf.c_cf_flags)
result = flags != 0
current_frame = space.getexecutioncontext().gettopframe_nohidden()
if current_frame:
codeflags = current_frame.pycode.co_flags
compilerflags = codeflags & PyCF_MASK
if compilerflags:
result = 1
flags |= compilerflags
# No future keyword at the moment
# if codeflags & CO_GENERATOR_ALLOWED:
# result = 1
# flags |= CO_GENERATOR_ALLOWED
cf.c_cf_flags = rffi.cast(rffi.INT, flags)
return result
@cpython_api([], rffi.INT_real, error=CANNOT_FAIL)
def Py_GetRecursionLimit(space):
from pypy.module.sys.vm import getrecursionlimit
return space.int_w(getrecursionlimit(space))
@cpython_api([rffi.INT_real], lltype.Void, error=CANNOT_FAIL)
def Py_SetRecursionLimit(space, limit):
from pypy.module.sys.vm import setrecursionlimit
setrecursionlimit(space, widen(limit))
limit = 0 # for testing
@cpython_api([CONST_STRING], rffi.INT_real, error=1)
def Py_EnterRecursiveCall(space, where):
"""Marks a point where a recursive C-level call is about to be performed.
If USE_STACKCHECK is defined, this function checks if the the OS
stack overflowed using PyOS_CheckStack(). In this is the case, it
sets a MemoryError and returns a nonzero value.
The function then checks if the recursion limit is reached. If this is the
case, a RuntimeError is set and a nonzero value is returned.
Otherwise, zero is returned.
where should be a string such as " in instance check" to be
concatenated to the RuntimeError message caused by the recursion depth
limit."""
if not we_are_translated():
# XXX hack since the stack checks only work translated
global limit
limit += 1
if limit > 10:
raise oefmt(space.w_RecursionError,
"maximum recursion depth exceeded%s", rffi.charp2str(where))
return 0
from rpython.rlib.rstack import stack_almost_full
if stack_almost_full():
raise oefmt(space.w_RecursionError,
"maximum recursion depth exceeded%s", rffi.charp2str(where))
return 0
@cpython_api([], lltype.Void)
def Py_LeaveRecursiveCall(space):
"""Ends a Py_EnterRecursiveCall(). Must be called once for each
successful invocation of Py_EnterRecursiveCall()."""
# A NOP in PyPy
if not we_are_translated():
limit = 0
@cpython_api([], PyObject, result_borrowed=True)
def _PyEval_GetAsyncGenFirstiter(space):
# sys.get_asyncgen_hooks().firstiter
ec = space.getexecutioncontext()
return ec.w_asyncgen_firstiter_fn
@cpython_api([], PyObject, result_borrowed=True)
def _PyEval_GetAsyncGenFinalizer(space):
# sys.get_asyncgen_hooks().finalizer
ec = space.getexecutioncontext()
return ec.w_asyncgen_finalizer_fn
@cpython_api([PyObject], rffi.CONST_CCHARP)
def PyEval_GetFuncName(space, w_obj):
try:
w_name = space.getattr(w_obj, space.newtext('__name__'))
name = space.utf8_w(w_name)
except OperationError as e:
e.write_unraisable(space, "PyEval_GetFuncName")
name = "unknown"
# leak the allocation. We could track this in a cache, and free it at
# shutdown.
return rffi.str2constcharp(name, track_allocation=False)
|