diff --git a/Doc/whatsnew/3.6.rst b/Doc/whatsnew/3.6.rst index 031bf797b8f..085bca31dd4 100644 --- a/Doc/whatsnew/3.6.rst +++ b/Doc/whatsnew/3.6.rst @@ -97,6 +97,34 @@ Windows improvements: New Features ============ +.. _pep-523: + +PEP 523: Adding a frame evaluation API to CPython +================================================= + +While Python provides extensive support to customize how code +executes, one place it has not done so is in the evaluation of frame +objects. If you wanted some way to intercept frame evaluation in +Python there really wasn't any way without directly manipulating +function pointers for defined functions. + +:pep:`523` changes this by providing an API to make frame +evaluation pluggable at the C level. This will allow for tools such +as debuggers and JITs to intercept frame evaluation before the +execution of Python code begins. This enables the use of alternative +evaluation implementations for Python code, tracking frame +evaluation, etc. + +This API is not part of the limited C API and is marked as private to +signal that usage of this API is expected to be limited and only +applicable to very select, low-level use-cases. + +.. seealso:: + + :pep:`523` - Adding a frame evaluation API to CPython + PEP written by Brett Cannon and Dino Viehland. + + .. _pep-519: PEP 519: Adding a file system path protocol diff --git a/Include/ceval.h b/Include/ceval.h index 73b4ca63282..7607f75dcab 100644 --- a/Include/ceval.h +++ b/Include/ceval.h @@ -119,6 +119,9 @@ PyAPI_FUNC(const char *) PyEval_GetFuncDesc(PyObject *); PyAPI_FUNC(PyObject *) PyEval_GetCallStats(PyObject *); PyAPI_FUNC(PyObject *) PyEval_EvalFrame(struct _frame *); PyAPI_FUNC(PyObject *) PyEval_EvalFrameEx(struct _frame *f, int exc); +#ifndef Py_LIMITED_API +PyAPI_FUNC(PyObject *) _PyEval_EvalFrameDefault(struct _frame *f, int exc); +#endif /* Interface for threads. diff --git a/Include/pystate.h b/Include/pystate.h index f08618cc00f..5a067736e37 100644 --- a/Include/pystate.h +++ b/Include/pystate.h @@ -12,10 +12,13 @@ extern "C" { struct _ts; /* Forward */ struct _is; /* Forward */ +struct _frame; /* Forward declaration for PyFrameObject. */ #ifdef Py_LIMITED_API typedef struct _is PyInterpreterState; #else +typedef PyObject* (*_PyFrameEvalFunction)(struct _frame *, int); + typedef struct _is { struct _is *next; @@ -42,14 +45,14 @@ typedef struct _is { PyObject *builtins_copy; PyObject *import_func; + /* Initialized to PyEval_EvalFrameDefault(). */ + _PyFrameEvalFunction eval_frame; } PyInterpreterState; #endif /* State unique per thread */ -struct _frame; /* Avoid including frameobject.h */ - #ifndef Py_LIMITED_API /* Py_tracefunc return -1 when raising an exception, or 0 for success. */ typedef int (*Py_tracefunc)(PyObject *, struct _frame *, int, PyObject *); diff --git a/Misc/NEWS b/Misc/NEWS index f9abe29807b..c411531341b 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -17,7 +17,9 @@ Core and Builtins restriction: in beta 2, backslashes will only be disallowed inside the braces (where the expressions are). This is a breaking change from the 3.6 alpha releases. - + +- Implement the frame evaluation part of PEP 523. + - Issue #27870: A left shift of zero by a large integer no longer attempts to allocate large amounts of memory. diff --git a/Python/ceval.c b/Python/ceval.c index 5a542f00639..05563a001c2 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -796,6 +796,13 @@ PyEval_EvalFrame(PyFrameObject *f) { PyObject * PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) +{ + PyThreadState *tstate = PyThreadState_GET(); + return tstate->interp->eval_frame(f, throwflag); +} + +PyObject * +_PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) { #ifdef DXPAIRS int lastopcode = 0; diff --git a/Python/pystate.c b/Python/pystate.c index 25110b287ff..61bda2a9fbb 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -91,6 +91,7 @@ PyInterpreterState_New(void) interp->fscodec_initialized = 0; interp->importlib = NULL; interp->import_func = NULL; + interp->eval_frame = _PyEval_EvalFrameDefault; #ifdef HAVE_DLOPEN #if HAVE_DECL_RTLD_NOW interp->dlopenflags = RTLD_NOW;