Skip to content

Commit 0b72b23

Browse files
authored
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().
1 parent c846ef0 commit 0b72b23

File tree

9 files changed

+67
-13
lines changed

9 files changed

+67
-13
lines changed

Doc/c-api/init.rst

+26
Original file line numberDiff line numberDiff line change
@@ -1091,6 +1091,32 @@ All of the following functions must be called after :c:func:`Py_Initialize`.
10911091
10921092
.. versionadded:: 3.8
10931093
1094+
.. c:type:: PyObject* (*_PyFrameEvalFunction)(PyThreadState *tstate, PyFrameObject *frame, int throwflag)
1095+
1096+
Type of a frame evaluation function.
1097+
1098+
The *throwflag* parameter is used by the ``throw()`` method of generators:
1099+
if non-zero, handle the current exception.
1100+
1101+
.. versionchanged:: 3.9
1102+
The function now takes a *tstate* parameter.
1103+
1104+
.. c:function:: _PyFrameEvalFunction _PyInterpreterState_GetEvalFrameFunc(PyInterpreterState *interp)
1105+
1106+
Get the frame evaluation function.
1107+
1108+
See the :pep:`523` "Adding a frame evaluation API to CPython".
1109+
1110+
.. versionadded:: 3.9
1111+
1112+
.. c:function:: void _PyInterpreterState_SetEvalFrameFunc(PyInterpreterState *interp, _PyFrameEvalFunction eval_frame);
1113+
1114+
Set the frame evaluation function.
1115+
1116+
See the :pep:`523` "Adding a frame evaluation API to CPython".
1117+
1118+
.. versionadded:: 3.9
1119+
10941120
10951121
.. c:function:: PyObject* PyThreadState_GetDict()
10961122

Doc/whatsnew/3.9.rst

+4
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,10 @@ Build and C API Changes
487487

488488
(Contributed by Victor Stinner in :issue:`38644` and :issue:`39542`.)
489489

490+
* ``PyInterpreterState.eval_frame`` (:pep:`523`) now requires a new mandatory
491+
*tstate* parameter (``PyThreadState*``).
492+
(Contributed by Victor Stinner in :issue:`38500`.)
493+
490494

491495
Deprecated
492496
==========

Include/cpython/ceval.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ PyAPI_FUNC(PyObject *) _PyEval_GetBuiltinId(_Py_Identifier *);
2121
flag was set, else return 0. */
2222
PyAPI_FUNC(int) PyEval_MergeCompilerFlags(PyCompilerFlags *cf);
2323

24-
PyAPI_FUNC(PyObject *) _PyEval_EvalFrameDefault(struct _frame *f, int exc);
24+
PyAPI_FUNC(PyObject *) _PyEval_EvalFrameDefault(PyThreadState *tstate, struct _frame *f, int exc);
2525

2626
PyAPI_FUNC(void) _PyEval_SetSwitchInterval(unsigned long microseconds);
2727
PyAPI_FUNC(unsigned long) _PyEval_GetSwitchInterval(void);

Include/cpython/pystate.h

+10
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,16 @@ PyAPI_FUNC(void) PyThreadState_DeleteCurrent(void);
186186

187187
typedef struct _frame *(*PyThreadFrameGetter)(PyThreadState *self_);
188188

189+
/* Frame evaluation API */
190+
191+
typedef PyObject* (*_PyFrameEvalFunction)(PyThreadState *tstate, struct _frame *, int);
192+
193+
PyAPI_FUNC(_PyFrameEvalFunction) _PyInterpreterState_GetEvalFrameFunc(
194+
PyInterpreterState *interp);
195+
PyAPI_FUNC(void) _PyInterpreterState_SetEvalFrameFunc(
196+
PyInterpreterState *interp,
197+
_PyFrameEvalFunction eval_frame);
198+
189199
/* cross-interpreter data */
190200

191201
struct _xid;

Include/internal/pycore_ceval.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ void _PyEval_Fini(void);
4040
static inline PyObject*
4141
_PyEval_EvalFrame(PyThreadState *tstate, struct _frame *f, int throwflag)
4242
{
43-
return tstate->interp->eval_frame(f, throwflag);
43+
return tstate->interp->eval_frame(tstate, f, throwflag);
4444
}
4545

4646
extern PyObject *_PyEval_EvalCode(

Include/internal/pycore_pystate.h

-2
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,6 @@ struct _ceval_runtime_state {
5454

5555
/* interpreter state */
5656

57-
typedef PyObject* (*_PyFrameEvalFunction)(struct _frame *, int);
58-
5957
#define _PY_NSMALLPOSINTS 257
6058
#define _PY_NSMALLNEGINTS 5
6159

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

Python/ceval.c

+6-9
Original file line numberDiff line numberDiff line change
@@ -725,9 +725,7 @@ PyEval_EvalCode(PyObject *co, PyObject *globals, PyObject *locals)
725725
PyObject *
726726
PyEval_EvalFrame(PyFrameObject *f)
727727
{
728-
/* This is for backward compatibility with extension modules that
729-
used this API; core interpreter code should call
730-
PyEval_EvalFrameEx() */
728+
/* Function kept for backward compatibility */
731729
PyThreadState *tstate = _PyThreadState_GET();
732730
return _PyEval_EvalFrame(tstate, f, 0);
733731
}
@@ -740,8 +738,10 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
740738
}
741739

742740
PyObject* _Py_HOT_FUNCTION
743-
_PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
741+
_PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
744742
{
743+
ensure_tstate_not_null(__func__, tstate);
744+
745745
#ifdef DXPAIRS
746746
int lastopcode = 0;
747747
#endif
@@ -756,9 +756,6 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
756756
_Py_atomic_int * const eval_breaker = &ceval->eval_breaker;
757757
PyCodeObject *co;
758758

759-
PyThreadState * const tstate = _PyRuntimeState_GetThreadState(runtime);
760-
ensure_tstate_not_null(__func__, tstate);
761-
762759
/* when tracing we set things up so that
763760
764761
not (instr_lb <= current_bytecode_offset < instr_ub)
@@ -1181,7 +1178,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
11811178
goto error;
11821179

11831180
#ifdef Py_DEBUG
1184-
/* PyEval_EvalFrameEx() must not be called with an exception set,
1181+
/* _PyEval_EvalFrameDefault() must not be called with an exception set,
11851182
because it can clear it (directly or indirectly) and so the
11861183
caller loses its exception */
11871184
assert(!_PyErr_Occurred(tstate));
@@ -3702,7 +3699,7 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
37023699
f->f_executing = 0;
37033700
tstate->frame = f->f_back;
37043701

3705-
return _Py_CheckFunctionResult(tstate, NULL, retval, "PyEval_EvalFrameEx");
3702+
return _Py_CheckFunctionResult(tstate, NULL, retval, __func__);
37063703
}
37073704

37083705
static void

Python/pystate.c

+14
Original file line numberDiff line numberDiff line change
@@ -1722,6 +1722,20 @@ _register_builtins_for_crossinterpreter_data(struct _xidregistry *xidregistry)
17221722
}
17231723

17241724

1725+
_PyFrameEvalFunction
1726+
_PyInterpreterState_GetEvalFrameFunc(PyInterpreterState *interp)
1727+
{
1728+
return interp->eval_frame;
1729+
}
1730+
1731+
1732+
void
1733+
_PyInterpreterState_SetEvalFrameFunc(PyInterpreterState *interp,
1734+
_PyFrameEvalFunction eval_frame)
1735+
{
1736+
interp->eval_frame = eval_frame;
1737+
}
1738+
17251739
#ifdef __cplusplus
17261740
}
17271741
#endif

0 commit comments

Comments
 (0)