bpo-38500: Add _PyInterpreterState_SetEvalFrameFunc() (GH-17340)

PyInterpreterState.eval_frame function now requires a tstate (Python
thread state) parameter.

Add private functions to the C API to get and set the frame
evaluation function:

* Add tstate parameter to _PyFrameEvalFunction function type.
* Add _PyInterpreterState_GetEvalFrameFunc() and
  _PyInterpreterState_SetEvalFrameFunc() functions.
* Add tstate parameter to _PyEval_EvalFrameDefault().
This commit is contained in:
Victor Stinner 2020-03-12 23:18:39 +01:00 committed by GitHub
parent c846ef004d
commit 0b72b23fb0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 67 additions and 13 deletions

View file

@ -1091,6 +1091,32 @@ All of the following functions must be called after :c:func:`Py_Initialize`.
.. versionadded:: 3.8
.. c:type:: PyObject* (*_PyFrameEvalFunction)(PyThreadState *tstate, PyFrameObject *frame, int throwflag)
Type of a frame evaluation function.
The *throwflag* parameter is used by the ``throw()`` method of generators:
if non-zero, handle the current exception.
.. versionchanged:: 3.9
The function now takes a *tstate* parameter.
.. c:function:: _PyFrameEvalFunction _PyInterpreterState_GetEvalFrameFunc(PyInterpreterState *interp)
Get the frame evaluation function.
See the :pep:`523` "Adding a frame evaluation API to CPython".
.. versionadded:: 3.9
.. c:function:: void _PyInterpreterState_SetEvalFrameFunc(PyInterpreterState *interp, _PyFrameEvalFunction eval_frame);
Set the frame evaluation function.
See the :pep:`523` "Adding a frame evaluation API to CPython".
.. versionadded:: 3.9
.. c:function:: PyObject* PyThreadState_GetDict()

View file

@ -487,6 +487,10 @@ Build and C API Changes
(Contributed by Victor Stinner in :issue:`38644` and :issue:`39542`.)
* ``PyInterpreterState.eval_frame`` (:pep:`523`) now requires a new mandatory
*tstate* parameter (``PyThreadState*``).
(Contributed by Victor Stinner in :issue:`38500`.)
Deprecated
==========

View file

@ -21,7 +21,7 @@ PyAPI_FUNC(PyObject *) _PyEval_GetBuiltinId(_Py_Identifier *);
flag was set, else return 0. */
PyAPI_FUNC(int) PyEval_MergeCompilerFlags(PyCompilerFlags *cf);
PyAPI_FUNC(PyObject *) _PyEval_EvalFrameDefault(struct _frame *f, int exc);
PyAPI_FUNC(PyObject *) _PyEval_EvalFrameDefault(PyThreadState *tstate, struct _frame *f, int exc);
PyAPI_FUNC(void) _PyEval_SetSwitchInterval(unsigned long microseconds);
PyAPI_FUNC(unsigned long) _PyEval_GetSwitchInterval(void);

View file

@ -186,6 +186,16 @@ PyAPI_FUNC(void) PyThreadState_DeleteCurrent(void);
typedef struct _frame *(*PyThreadFrameGetter)(PyThreadState *self_);
/* Frame evaluation API */
typedef PyObject* (*_PyFrameEvalFunction)(PyThreadState *tstate, struct _frame *, int);
PyAPI_FUNC(_PyFrameEvalFunction) _PyInterpreterState_GetEvalFrameFunc(
PyInterpreterState *interp);
PyAPI_FUNC(void) _PyInterpreterState_SetEvalFrameFunc(
PyInterpreterState *interp,
_PyFrameEvalFunction eval_frame);
/* cross-interpreter data */
struct _xid;

View file

@ -40,7 +40,7 @@ void _PyEval_Fini(void);
static inline PyObject*
_PyEval_EvalFrame(PyThreadState *tstate, struct _frame *f, int throwflag)
{
return tstate->interp->eval_frame(f, throwflag);
return tstate->interp->eval_frame(tstate, f, throwflag);
}
extern PyObject *_PyEval_EvalCode(

View file

@ -54,8 +54,6 @@ struct _ceval_runtime_state {
/* interpreter state */
typedef PyObject* (*_PyFrameEvalFunction)(struct _frame *, int);
#define _PY_NSMALLPOSINTS 257
#define _PY_NSMALLNEGINTS 5

View file

@ -0,0 +1,5 @@
Add a private API to get and set the frame evaluation function: add
:c:func:`_PyInterpreterState_GetEvalFrameFunc` and
:c:func:`_PyInterpreterState_SetEvalFrameFunc` C functions.
The :c:type:`_PyFrameEvalFunction` function type now takes a *tstate*
parameter.

View file

@ -725,9 +725,7 @@ PyEval_EvalCode(PyObject *co, PyObject *globals, PyObject *locals)
PyObject *
PyEval_EvalFrame(PyFrameObject *f)
{
/* This is for backward compatibility with extension modules that
used this API; core interpreter code should call
PyEval_EvalFrameEx() */
/* Function kept for backward compatibility */
PyThreadState *tstate = _PyThreadState_GET();
return _PyEval_EvalFrame(tstate, f, 0);
}
@ -740,8 +738,10 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
}
PyObject* _Py_HOT_FUNCTION
_PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
_PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
{
ensure_tstate_not_null(__func__, tstate);
#ifdef DXPAIRS
int lastopcode = 0;
#endif
@ -756,9 +756,6 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
_Py_atomic_int * const eval_breaker = &ceval->eval_breaker;
PyCodeObject *co;
PyThreadState * const tstate = _PyRuntimeState_GetThreadState(runtime);
ensure_tstate_not_null(__func__, tstate);
/* when tracing we set things up so that
not (instr_lb <= current_bytecode_offset < instr_ub)
@ -1181,7 +1178,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
goto error;
#ifdef Py_DEBUG
/* PyEval_EvalFrameEx() must not be called with an exception set,
/* _PyEval_EvalFrameDefault() must not be called with an exception set,
because it can clear it (directly or indirectly) and so the
caller loses its exception */
assert(!_PyErr_Occurred(tstate));
@ -3702,7 +3699,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
f->f_executing = 0;
tstate->frame = f->f_back;
return _Py_CheckFunctionResult(tstate, NULL, retval, "PyEval_EvalFrameEx");
return _Py_CheckFunctionResult(tstate, NULL, retval, __func__);
}
static void

View file

@ -1722,6 +1722,20 @@ _register_builtins_for_crossinterpreter_data(struct _xidregistry *xidregistry)
}
_PyFrameEvalFunction
_PyInterpreterState_GetEvalFrameFunc(PyInterpreterState *interp)
{
return interp->eval_frame;
}
void
_PyInterpreterState_SetEvalFrameFunc(PyInterpreterState *interp,
_PyFrameEvalFunction eval_frame)
{
interp->eval_frame = eval_frame;
}
#ifdef __cplusplus
}
#endif