Skip to content
Prev Previous commit
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().
  • Loading branch information
vstinner authored and petdance committed Mar 13, 2020
commit 43db5c9da373c21f0841f7045af2637f0d8875f3
26 changes: 26 additions & 0 deletions Doc/c-api/init.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down
4 changes: 4 additions & 0 deletions Doc/whatsnew/3.9.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
==========
Expand Down
2 changes: 1 addition & 1 deletion Include/cpython/ceval.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
10 changes: 10 additions & 0 deletions Include/cpython/pystate.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion Include/internal/pycore_ceval.h
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
2 changes: 0 additions & 2 deletions Include/internal/pycore_pystate.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,6 @@ struct _ceval_runtime_state {

/* interpreter state */

typedef PyObject* (*_PyFrameEvalFunction)(struct _frame *, int);

#define _PY_NSMALLPOSINTS 257
#define _PY_NSMALLNEGINTS 5

Expand Down
Original file line number Diff line number Diff line change
@@ -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.
15 changes: 6 additions & 9 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand All @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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
Expand Down
14 changes: 14 additions & 0 deletions Python/pystate.c
Original file line number Diff line number Diff line change
Expand Up @@ -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