Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Unstable C API tier: Convert the code functions
  • Loading branch information
encukou committed Jan 17, 2023
commit 1b0b8cd6c27c2168ab505e18a579996e27338b24
98 changes: 92 additions & 6 deletions Doc/c-api/code.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,28 +33,47 @@ bound into a function.

Return the number of free variables in *co*.

.. c:function:: PyCodeObject* PyCode_New(int argcount, int kwonlyargcount, int nlocals, int stacksize, int flags, PyObject *code, PyObject *consts, PyObject *names, PyObject *varnames, PyObject *freevars, PyObject *cellvars, PyObject *filename, PyObject *name, int firstlineno, PyObject *linetable, PyObject *exceptiontable)
.. c:function:: PyCodeObject* PyUnstable_Code_New(int argcount, int kwonlyargcount, int nlocals, int stacksize, int flags, PyObject *code, PyObject *consts, PyObject *names, PyObject *varnames, PyObject *freevars, PyObject *cellvars, PyObject *filename, PyObject *name, int firstlineno, PyObject *linetable, PyObject *exceptiontable)

Return a new code object. If you need a dummy code object to create a frame,
use :c:func:`PyCode_NewEmpty` instead. Calling :c:func:`PyCode_New` directly
will bind you to a precise Python version since the definition of the bytecode
changes often. The many arguments of this function are inter-dependent in complex
use :c:func:`PyCode_NewEmpty` instead.

Since the definition of the bytecode changes often, calling
:c:func:`PyCode_New` directly can bind you to a precise Python version.

The many arguments of this function are inter-dependent in complex
ways, meaning that subtle changes to values are likely to result in incorrect
execution or VM crashes. Use this function only with extreme care.

.. versionchanged:: 3.11
Added ``exceptiontable`` parameter.

.. c:function:: PyCodeObject* PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount, int nlocals, int stacksize, int flags, PyObject *code, PyObject *consts, PyObject *names, PyObject *varnames, PyObject *freevars, PyObject *cellvars, PyObject *filename, PyObject *name, int firstlineno, PyObject *linetable, PyObject *exceptiontable)
.. index:: single: PyCode_New

.. versionchanged:: 3.12

Renamed from ``PyCode_New`` as part of :ref:`unstable-c-api`.
The old name is deprecated, but will remain available until the
signature changes again.

.. c:function:: PyCodeObject* PyUnstable_Code_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount, int nlocals, int stacksize, int flags, PyObject *code, PyObject *consts, PyObject *names, PyObject *varnames, PyObject *freevars, PyObject *cellvars, PyObject *filename, PyObject *name, int firstlineno, PyObject *linetable, PyObject *exceptiontable)

Similar to :c:func:`PyCode_New`, but with an extra "posonlyargcount" for positional-only arguments.
The same caveats that apply to ``PyCode_New`` also apply to this function.

.. versionadded:: 3.8
.. index:: single: PyCode_NewWithPosOnlyArgs

.. versionadded:: 3.8 as ``PyCode_NewWithPosOnlyArgs``

.. versionchanged:: 3.11
Added ``exceptiontable`` parameter.

.. versionchanged:: 3.12

Renamed to ``PyUnstable_Code_NewWithPosOnlyArgs``.
The old name is deprecated, but will remain available until the
signature changes again.

.. c:function:: PyCodeObject* PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno)

Return a new empty code object with the specified filename,
Expand Down Expand Up @@ -163,3 +182,70 @@ bound into a function.
:c:func:`PyErr_WriteUnraisable`. Otherwise it should return ``0``.

.. versionadded:: 3.12


Extra information
-----------------

To support low-level extensions to frame evaluation, such as external
just-in-time compilers, it is possible to attach arbitrary extra data to
code objects.

These functions are part of the unstable C API tier:
this functionality is a CPython implementation detail, and the API
may change without deprecation warnings.

.. c:function:: Py_ssize_t PyUnstable_Eval_RequestCodeExtraIndex(freefunc free)

Return a new an opaque index value used to adding data to code objects.

You generally call this function once (per interpreter) and use the result
with ``PyCode_GetExtra`` and ``PyCode_SetExtra`` to manipulate
data on individual code objects.

If *free* is not ``NULL``: when a code object is deallocated,
*free* will be called on non-``NULL`` data stored under the new index.
Use :c:func:`Py_DecRef` when storing :c:type:`PyObject`.

.. index:: single: _PyEval_RequestCodeExtraIndex

.. versionadded:: 3.6 as ``_PyEval_RequestCodeExtraIndex``

.. versionchanged:: 3.12

Renamed to ``PyUnstable_Eval_RequestCodeExtraIndex``.
The old private name is deprecated, but will be available until the API
changes.

.. c:function:: int PyUnstable_Code_GetExtra(PyObject *code, Py_ssize_t index, void **extra)

Set *extra* to the extra data stored under the given index.
Return 0 on success. Set an exception and return -1 on failure.

If no data was set under the index, set *extra* to ``NULL`` and return
0 without setting an exception.

.. index:: single: _PyCode_GetExtra

.. versionadded:: 3.6 as ``_PyCode_GetExtra``

.. versionchanged:: 3.12

Renamed to ``PyUnstable_Code_GetExtra``.
The old private name is deprecated, but will be available until the API
changes.

.. c:function:: int PyUnstable_Code_SetExtra(PyObject *code, Py_ssize_t index, void *extra)

Set the extra data stored under the given index to *extra*.
Return 0 on success. Set an exception and return -1 on failure.

.. index:: single: _PyCode_SetExtra

.. versionadded:: 3.6 as ``_PyCode_SetExtra``

.. versionchanged:: 3.12

Renamed to ``PyUnstable_Code_SetExtra``.
The old private name is deprecated, but will be available until the API
changes.
11 changes: 10 additions & 1 deletion Doc/whatsnew/3.12.rst
Original file line number Diff line number Diff line change
Expand Up @@ -753,7 +753,16 @@ New Features
warnings.
Its contents are marked by the ``PyUnstable_`` prefix in names.

(List TBD)
Code object constructors:

- ``PyUnstable_Code_New()`` (renamed from ``PyCode_New``)
- ``PyUnstable_Code_NewWithPosOnlyArgs()`` (renamed from ``PyCode_NewWithPosOnlyArgs``)

Extra storage for code objects (:pep:`523`):

- ``PyUnstable_Eval_RequestCodeExtraIndex()`` (renamed from ``_PyEval_RequestCodeExtraIndex``)
- ``PyUnstable_Code_GetExtra()`` (renamed from ``_PyCode_GetExtra``)
- ``PyUnstable_Code_SetExtra()`` (renamed from ``_PyCode_SetExtra``)

The original names will continue to be available until the respective
API changes.
Expand Down
7 changes: 6 additions & 1 deletion Include/cpython/ceval.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,12 @@ PyAPI_FUNC(PyObject *) _PyEval_EvalFrameDefault(PyThreadState *tstate, struct _P
PyAPI_FUNC(void) _PyEval_SetSwitchInterval(unsigned long microseconds);
PyAPI_FUNC(unsigned long) _PyEval_GetSwitchInterval(void);

PyAPI_FUNC(Py_ssize_t) _PyEval_RequestCodeExtraIndex(freefunc);
PyAPI_FUNC(Py_ssize_t) PyUnstable_Eval_RequestCodeExtraIndex(freefunc);
// Old name -- remove when this API changes:
_Py_DEPRECATED_EXTERNALLY(3.12) static inline Py_ssize_t
_PyEval_RequestCodeExtraIndex(freefunc f) {
return PyUnstable_Eval_RequestCodeExtraIndex(f);
}

PyAPI_FUNC(int) _PyEval_SliceIndex(PyObject *, Py_ssize_t *);
PyAPI_FUNC(int) _PyEval_SliceIndexNotNone(PyObject *, Py_ssize_t *);
47 changes: 39 additions & 8 deletions Include/cpython/code.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,19 +164,40 @@ static inline int PyCode_GetFirstFree(PyCodeObject *op) {
#define _PyCode_CODE(CO) _Py_RVALUE((_Py_CODEUNIT *)(CO)->co_code_adaptive)
#define _PyCode_NBYTES(CO) (Py_SIZE(CO) * (Py_ssize_t)sizeof(_Py_CODEUNIT))

/* Public interface */
PyAPI_FUNC(PyCodeObject *) PyCode_New(
/* Unstable public interface */
PyAPI_FUNC(PyCodeObject *) PyUnstable_Code_New(
int, int, int, int, int, PyObject *, PyObject *,
PyObject *, PyObject *, PyObject *, PyObject *,
PyObject *, PyObject *, PyObject *, int, PyObject *,
PyObject *);

PyAPI_FUNC(PyCodeObject *) PyCode_NewWithPosOnlyArgs(
PyAPI_FUNC(PyCodeObject *) PyUnstable_Code_NewWithPosOnlyArgs(
int, int, int, int, int, int, PyObject *, PyObject *,
PyObject *, PyObject *, PyObject *, PyObject *,
PyObject *, PyObject *, PyObject *, int, PyObject *,
PyObject *);
/* same as struct above */
// Old names -- remove when this API changes:
_Py_DEPRECATED_EXTERNALLY(3.12) static inline PyCodeObject *
PyCode_New(
int a, int b, int c, int d, int e, PyObject *f, PyObject *g,
PyObject *h, PyObject *i, PyObject *j, PyObject *k,
PyObject *l, PyObject *m, PyObject *n, int o, PyObject *p,
PyObject *q)
{
return PyUnstable_Code_New(
a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q);
}
_Py_DEPRECATED_EXTERNALLY(3.12) static inline PyCodeObject *
PyCode_NewWithPosOnlyArgs(
int a, int poac, int b, int c, int d, int e, PyObject *f, PyObject *g,
PyObject *h, PyObject *i, PyObject *j, PyObject *k,
PyObject *l, PyObject *m, PyObject *n, int o, PyObject *p,
PyObject *q)
{
return PyUnstable_Code_NewWithPosOnlyArgs(
a, poac, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q);
}

/* Creates a new empty code object with the specified source location. */
PyAPI_FUNC(PyCodeObject *)
Expand Down Expand Up @@ -255,11 +276,21 @@ PyAPI_FUNC(PyObject*) _PyCode_ConstantKey(PyObject *obj);
PyAPI_FUNC(PyObject*) PyCode_Optimize(PyObject *code, PyObject* consts,
PyObject *names, PyObject *lnotab);


PyAPI_FUNC(int) _PyCode_GetExtra(PyObject *code, Py_ssize_t index,
void **extra);
PyAPI_FUNC(int) _PyCode_SetExtra(PyObject *code, Py_ssize_t index,
void *extra);
PyAPI_FUNC(int) PyUnstable_Code_GetExtra(
PyObject *code, Py_ssize_t index, void **extra);
PyAPI_FUNC(int) PyUnstable_Code_SetExtra(
PyObject *code, Py_ssize_t index, void *extra);
// Old names -- remove when this API changes:
_Py_DEPRECATED_EXTERNALLY(3.12) static inline int
_PyCode_GetExtra(PyObject *code, Py_ssize_t index, void **extra)
{
return PyUnstable_Code_GetExtra(code, index, extra);
}
_Py_DEPRECATED_EXTERNALLY(3.12) static inline int
_PyCode_SetExtra(PyObject *code, Py_ssize_t index, void *extra)
{
return PyUnstable_Code_SetExtra(code, index, extra);
}

/* Equivalent to getattr(code, 'co_code') in Python.
Returns a strong reference to a bytes object. */
Expand Down
9 changes: 9 additions & 0 deletions Include/pyport.h
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,15 @@ extern "C" {
#define Py_DEPRECATED(VERSION_UNUSED)
#endif

// _Py_DEPRECATED_EXTERNALLY(version)
// Deprecated outside CPython core.
#ifdef Py_BUILD_CORE
#define _Py_DEPRECATED_EXTERNALLY(VERSION_UNUSED)
#else
#define _Py_DEPRECATED_EXTERNALLY(version) Py_DEPRECATED(version)
#endif


#if defined(__clang__)
#define _Py_COMP_DIAG_PUSH _Pragma("clang diagnostic push")
#define _Py_COMP_DIAG_IGNORE_DEPR_DECLS \
Expand Down
6 changes: 3 additions & 3 deletions Lib/test/test_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -751,15 +751,15 @@ def f():
py = ctypes.pythonapi
freefunc = ctypes.CFUNCTYPE(None,ctypes.c_voidp)

RequestCodeExtraIndex = py._PyEval_RequestCodeExtraIndex
RequestCodeExtraIndex = py.PyUnstable_Eval_RequestCodeExtraIndex
RequestCodeExtraIndex.argtypes = (freefunc,)
RequestCodeExtraIndex.restype = ctypes.c_ssize_t

SetExtra = py._PyCode_SetExtra
SetExtra = py.PyUnstable_Code_SetExtra
SetExtra.argtypes = (ctypes.py_object, ctypes.c_ssize_t, ctypes.c_voidp)
SetExtra.restype = ctypes.c_int

GetExtra = py._PyCode_GetExtra
GetExtra = py.PyUnstable_Code_GetExtra
GetExtra.argtypes = (ctypes.py_object, ctypes.c_ssize_t,
ctypes.POINTER(ctypes.c_voidp))
GetExtra.restype = ctypes.c_int
Expand Down
9 changes: 5 additions & 4 deletions Objects/codeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,8 @@ _PyCode_New(struct _PyCodeConstructor *con)
******************/

PyCodeObject *
PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount,
PyUnstable_Code_NewWithPosOnlyArgs(
int argcount, int posonlyargcount, int kwonlyargcount,
int nlocals, int stacksize, int flags,
PyObject *code, PyObject *consts, PyObject *names,
PyObject *varnames, PyObject *freevars, PyObject *cellvars,
Expand Down Expand Up @@ -691,7 +692,7 @@ PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount,
}

PyCodeObject *
PyCode_New(int argcount, int kwonlyargcount,
PyUnstable_Code_New(int argcount, int kwonlyargcount,
int nlocals, int stacksize, int flags,
PyObject *code, PyObject *consts, PyObject *names,
PyObject *varnames, PyObject *freevars, PyObject *cellvars,
Expand Down Expand Up @@ -1371,7 +1372,7 @@ typedef struct {


int
_PyCode_GetExtra(PyObject *code, Py_ssize_t index, void **extra)
PyUnstable_Code_GetExtra(PyObject *code, Py_ssize_t index, void **extra)
{
if (!PyCode_Check(code)) {
PyErr_BadInternalCall();
Expand All @@ -1392,7 +1393,7 @@ _PyCode_GetExtra(PyObject *code, Py_ssize_t index, void **extra)


int
_PyCode_SetExtra(PyObject *code, Py_ssize_t index, void *extra)
PyUnstable_Code_SetExtra(PyObject *code, Py_ssize_t index, void *extra)
{
PyInterpreterState *interp = _PyInterpreterState_GET();

Expand Down
2 changes: 1 addition & 1 deletion Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -3347,7 +3347,7 @@ format_awaitable_error(PyThreadState *tstate, PyTypeObject *type, int oparg)


Py_ssize_t
_PyEval_RequestCodeExtraIndex(freefunc free)
PyUnstable_Eval_RequestCodeExtraIndex(freefunc free)
{
PyInterpreterState *interp = _PyInterpreterState_GET();
Py_ssize_t new_index;
Expand Down