gh-101101: Unstable C API tier (PEP 689) (GH-101102)

This commit is contained in:
Petr Viktorin 2023-02-28 09:31:01 +01:00 committed by GitHub
parent c41af812c9
commit 6b2d7c0ddb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 358 additions and 29 deletions

View File

@ -33,28 +33,47 @@ bound into a function.
Return the number of free variables in *co*. 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, 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 use :c:func:`PyCode_NewEmpty` instead.
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 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 ways, meaning that subtle changes to values are likely to result in incorrect
execution or VM crashes. Use this function only with extreme care. execution or VM crashes. Use this function only with extreme care.
.. versionchanged:: 3.11 .. versionchanged:: 3.11
Added ``exceptiontable`` parameter. 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. 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. 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 .. versionchanged:: 3.11
Added ``exceptiontable`` parameter. 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) .. c:function:: PyCodeObject* PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno)
Return a new empty code object with the specified filename, Return a new empty code object with the specified filename,
@ -165,3 +184,70 @@ bound into a function.
:c:func:`PyErr_WriteUnraisable`. Otherwise it should return ``0``. :c:func:`PyErr_WriteUnraisable`. Otherwise it should return ``0``.
.. versionadded:: 3.12 .. 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.

View File

@ -6,9 +6,9 @@
C API Stability C API Stability
*************** ***************
Python's C API is covered by the Backwards Compatibility Policy, :pep:`387`. Unless documented otherwise, Python's C API is covered by the Backwards
While the C API will change with every minor release (e.g. from 3.9 to 3.10), Compatibility Policy, :pep:`387`.
most changes will be source-compatible, typically by only adding new API. Most changes to it are source-compatible (typically by only adding new API).
Changing existing API or removing API is only done after a deprecation period Changing existing API or removing API is only done after a deprecation period
or to fix serious issues. or to fix serious issues.
@ -18,8 +18,38 @@ way; see :ref:`stable-abi-platform` below).
So, code compiled for Python 3.10.0 will work on 3.10.8 and vice versa, So, code compiled for Python 3.10.0 will work on 3.10.8 and vice versa,
but will need to be compiled separately for 3.9.x and 3.10.x. but will need to be compiled separately for 3.9.x and 3.10.x.
There are two tiers of C API with different stability exepectations:
- *Unstable API*, may change in minor versions without a deprecation period.
It is marked by the ``PyUnstable`` prefix in names.
- *Limited API*, is compatible across several minor releases.
When :c:macro:`Py_LIMITED_API` is defined, only this subset is exposed
from ``Python.h``.
These are discussed in more detail below.
Names prefixed by an underscore, such as ``_Py_InternalState``, Names prefixed by an underscore, such as ``_Py_InternalState``,
are private API that can change without notice even in patch releases. are private API that can change without notice even in patch releases.
If you need to use this API, consider reaching out to
`CPython developers <https://discuss.python.org/c/core-dev/c-api/30>`_
to discuss adding public API for your use case.
.. _unstable-c-api:
Unstable C API
==============
.. index:: single: PyUnstable
Any API named with the ``PyUnstable`` prefix exposes CPython implementation
details, and may change in every minor release (e.g. from 3.9 to 3.10) without
any deprecation warnings.
However, it will not change in a bugfix release (e.g. from 3.10.0 to 3.10.1).
It is generally intended for specialized, low-level tools like debuggers.
Projects that use this API are expected to follow
CPython development and spend extra effort adjusting to changes.
Stable Application Binary Interface Stable Application Binary Interface

View File

@ -143,6 +143,22 @@ class Annotations:
' (Only some members are part of the stable ABI.)') ' (Only some members are part of the stable ABI.)')
node.insert(0, emph_node) node.insert(0, emph_node)
# Unstable API annotation.
if name.startswith('PyUnstable'):
warn_node = nodes.admonition(
classes=['unstable-c-api', 'warning'])
message = 'This is '
emph_node = nodes.emphasis(message, message)
ref_node = addnodes.pending_xref(
'Unstable API', refdomain="std",
reftarget='unstable-c-api',
reftype='ref', refexplicit="False")
ref_node += nodes.Text('Unstable API')
emph_node += ref_node
emph_node += nodes.Text('. It may change without warning in minor releases.')
warn_node += emph_node
node.insert(0, warn_node)
# Return value annotation # Return value annotation
if objtype != 'function': if objtype != 'function':
continue continue

View File

@ -810,6 +810,29 @@ C API Changes
New Features New Features
------------ ------------
* :pep:`697`: Introduced the :ref:`Unstable C API tier <unstable-c-api>`,
intended for low-level tools like debuggers and JIT compilers.
This API may change in each minor release of CPython without deprecation
warnings.
Its contents are marked by the ``PyUnstable_`` prefix in names.
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.
(Contributed by Petr Viktorin in :gh:`101101`.)
* Added the new limited C API function :c:func:`PyType_FromMetaclass`, * Added the new limited C API function :c:func:`PyType_FromMetaclass`,
which generalizes the existing :c:func:`PyType_FromModuleAndSpec` using which generalizes the existing :c:func:`PyType_FromModuleAndSpec` using
an additional metaclass argument. an additional metaclass argument.

View File

@ -1,11 +1,13 @@
The Python C API The Python C API
================ ================
The C API is divided into three sections: The C API is divided into these sections:
1. ``Include/``: Limited API 1. ``Include/``: Limited API
2. ``Include/cpython/``: CPython implementation details 2. ``Include/cpython/``: CPython implementation details
3. ``Include/internal/``: The internal API 3. ``Include/cpython/``, names with the ``PyUnstable_`` prefix: API that can
change between minor releases
4. ``Include/internal/``, and any name with ``_`` prefix: The internal API
Information on changing the C API is available `in the developer guide`_ Information on changing the C API is available `in the developer guide`_

View File

@ -22,7 +22,12 @@ PyAPI_FUNC(PyObject *) _PyEval_EvalFrameDefault(PyThreadState *tstate, struct _P
PyAPI_FUNC(void) _PyEval_SetSwitchInterval(unsigned long microseconds); PyAPI_FUNC(void) _PyEval_SetSwitchInterval(unsigned long microseconds);
PyAPI_FUNC(unsigned long) _PyEval_GetSwitchInterval(void); 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_SliceIndex(PyObject *, Py_ssize_t *);
PyAPI_FUNC(int) _PyEval_SliceIndexNotNone(PyObject *, Py_ssize_t *); PyAPI_FUNC(int) _PyEval_SliceIndexNotNone(PyObject *, Py_ssize_t *);

View File

@ -178,19 +178,40 @@ static inline int PyCode_GetFirstFree(PyCodeObject *op) {
#define _PyCode_CODE(CO) _Py_RVALUE((_Py_CODEUNIT *)(CO)->co_code_adaptive) #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)) #define _PyCode_NBYTES(CO) (Py_SIZE(CO) * (Py_ssize_t)sizeof(_Py_CODEUNIT))
/* Public interface */ /* Unstable public interface */
PyAPI_FUNC(PyCodeObject *) PyCode_New( PyAPI_FUNC(PyCodeObject *) PyUnstable_Code_New(
int, int, int, int, int, PyObject *, PyObject *, int, int, int, int, int, PyObject *, PyObject *,
PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *,
PyObject *, PyObject *, PyObject *, int, PyObject *, PyObject *, PyObject *, PyObject *, int, PyObject *,
PyObject *); PyObject *);
PyAPI_FUNC(PyCodeObject *) PyCode_NewWithPosOnlyArgs( PyAPI_FUNC(PyCodeObject *) PyUnstable_Code_NewWithPosOnlyArgs(
int, int, int, int, int, int, PyObject *, PyObject *, int, int, int, int, int, int, PyObject *, PyObject *,
PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *, PyObject *,
PyObject *, PyObject *, PyObject *, int, PyObject *, PyObject *, PyObject *, PyObject *, int, PyObject *,
PyObject *); PyObject *);
/* same as struct above */ /* 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. */ /* Creates a new empty code object with the specified source location. */
PyAPI_FUNC(PyCodeObject *) PyAPI_FUNC(PyCodeObject *)
@ -269,11 +290,21 @@ PyAPI_FUNC(PyObject*) _PyCode_ConstantKey(PyObject *obj);
PyAPI_FUNC(PyObject*) PyCode_Optimize(PyObject *code, PyObject* consts, PyAPI_FUNC(PyObject*) PyCode_Optimize(PyObject *code, PyObject* consts,
PyObject *names, PyObject *lnotab); PyObject *names, PyObject *lnotab);
PyAPI_FUNC(int) PyUnstable_Code_GetExtra(
PyAPI_FUNC(int) _PyCode_GetExtra(PyObject *code, Py_ssize_t index, PyObject *code, Py_ssize_t index, void **extra);
void **extra); PyAPI_FUNC(int) PyUnstable_Code_SetExtra(
PyAPI_FUNC(int) _PyCode_SetExtra(PyObject *code, Py_ssize_t index, PyObject *code, Py_ssize_t index, void *extra);
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. /* Equivalent to getattr(code, 'co_code') in Python.
Returns a strong reference to a bytes object. */ Returns a strong reference to a bytes object. */

View File

@ -323,6 +323,15 @@ extern "C" {
#define Py_DEPRECATED(VERSION_UNUSED) #define Py_DEPRECATED(VERSION_UNUSED)
#endif #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__) #if defined(__clang__)
#define _Py_COMP_DIAG_PUSH _Pragma("clang diagnostic push") #define _Py_COMP_DIAG_PUSH _Pragma("clang diagnostic push")
#define _Py_COMP_DIAG_IGNORE_DEPR_DECLS \ #define _Py_COMP_DIAG_IGNORE_DEPR_DECLS \

View File

@ -752,15 +752,15 @@ if check_impl_detail(cpython=True) and ctypes is not None:
py = ctypes.pythonapi py = ctypes.pythonapi
freefunc = ctypes.CFUNCTYPE(None,ctypes.c_voidp) freefunc = ctypes.CFUNCTYPE(None,ctypes.c_voidp)
RequestCodeExtraIndex = py._PyEval_RequestCodeExtraIndex RequestCodeExtraIndex = py.PyUnstable_Eval_RequestCodeExtraIndex
RequestCodeExtraIndex.argtypes = (freefunc,) RequestCodeExtraIndex.argtypes = (freefunc,)
RequestCodeExtraIndex.restype = ctypes.c_ssize_t 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.argtypes = (ctypes.py_object, ctypes.c_ssize_t, ctypes.c_voidp)
SetExtra.restype = ctypes.c_int SetExtra.restype = ctypes.c_int
GetExtra = py._PyCode_GetExtra GetExtra = py.PyUnstable_Code_GetExtra
GetExtra.argtypes = (ctypes.py_object, ctypes.c_ssize_t, GetExtra.argtypes = (ctypes.py_object, ctypes.c_ssize_t,
ctypes.POINTER(ctypes.c_voidp)) ctypes.POINTER(ctypes.c_voidp))
GetExtra.restype = ctypes.c_int GetExtra.restype = ctypes.c_int

View File

@ -0,0 +1,3 @@
Introduced the *Unstable C API tier*, marking APi that is allowed to change
in minor releases without a deprecation period.
See :pep:`689` for details.

View File

@ -169,7 +169,7 @@
@MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c @MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c
@MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c @MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
@MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c @MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/vectorcall_limited.c _testcapi/heaptype.c _testcapi/unicode.c _testcapi/getargs.c _testcapi/pytime.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/structmember.c _testcapi/exceptions.c @MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/vectorcall_limited.c _testcapi/heaptype.c _testcapi/unicode.c _testcapi/getargs.c _testcapi/pytime.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c
@MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c @MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c
# Some testing modules MUST be built as shared libraries. # Some testing modules MUST be built as shared libraries.

115
Modules/_testcapi/code.c Normal file
View File

@ -0,0 +1,115 @@
#include "parts.h"
static Py_ssize_t
get_code_extra_index(PyInterpreterState* interp) {
Py_ssize_t result = -1;
static const char *key = "_testcapi.frame_evaluation.code_index";
PyObject *interp_dict = PyInterpreterState_GetDict(interp); // borrowed
assert(interp_dict); // real users would handle missing dict... somehow
PyObject *index_obj = PyDict_GetItemString(interp_dict, key); // borrowed
Py_ssize_t index = 0;
if (!index_obj) {
if (PyErr_Occurred()) {
goto finally;
}
index = PyUnstable_Eval_RequestCodeExtraIndex(NULL);
if (index < 0 || PyErr_Occurred()) {
goto finally;
}
index_obj = PyLong_FromSsize_t(index); // strong ref
if (!index_obj) {
goto finally;
}
int res = PyDict_SetItemString(interp_dict, key, index_obj);
Py_DECREF(index_obj);
if (res < 0) {
goto finally;
}
}
else {
index = PyLong_AsSsize_t(index_obj);
if (index == -1 && PyErr_Occurred()) {
goto finally;
}
}
result = index;
finally:
return result;
}
static PyObject *
test_code_extra(PyObject* self, PyObject *Py_UNUSED(callable))
{
PyObject *result = NULL;
PyObject *test_module = NULL;
PyObject *test_func = NULL;
// Get or initialize interpreter-specific code object storage index
PyInterpreterState *interp = PyInterpreterState_Get();
if (!interp) {
return NULL;
}
Py_ssize_t code_extra_index = get_code_extra_index(interp);
if (PyErr_Occurred()) {
goto finally;
}
// Get a function to test with
// This can be any Python function. Use `test.test_misc.testfunction`.
test_module = PyImport_ImportModule("test.test_capi.test_misc");
if (!test_module) {
goto finally;
}
test_func = PyObject_GetAttrString(test_module, "testfunction");
if (!test_func) {
goto finally;
}
PyObject *test_func_code = PyFunction_GetCode(test_func); // borrowed
if (!test_func_code) {
goto finally;
}
// Check the value is initially NULL
void *extra;
int res = PyUnstable_Code_GetExtra(test_func_code, code_extra_index, &extra);
if (res < 0) {
goto finally;
}
assert (extra == NULL);
// Set another code extra value
res = PyUnstable_Code_SetExtra(test_func_code, code_extra_index, (void*)(uintptr_t)77);
if (res < 0) {
goto finally;
}
// Assert it was set correctly
res = PyUnstable_Code_GetExtra(test_func_code, code_extra_index, &extra);
if (res < 0) {
goto finally;
}
assert ((uintptr_t)extra == 77);
result = Py_NewRef(Py_None);
finally:
Py_XDECREF(test_module);
Py_XDECREF(test_func);
return result;
}
static PyMethodDef TestMethods[] = {
{"test_code_extra", test_code_extra, METH_NOARGS},
{NULL},
};
int
_PyTestCapi_Init_Code(PyObject *m) {
if (PyModule_AddFunctions(m, TestMethods) < 0) {
return -1;
}
return 0;
}

View File

@ -37,6 +37,7 @@ int _PyTestCapi_Init_Long(PyObject *module);
int _PyTestCapi_Init_Float(PyObject *module); int _PyTestCapi_Init_Float(PyObject *module);
int _PyTestCapi_Init_Structmember(PyObject *module); int _PyTestCapi_Init_Structmember(PyObject *module);
int _PyTestCapi_Init_Exceptions(PyObject *module); int _PyTestCapi_Init_Exceptions(PyObject *module);
int _PyTestCapi_Init_Code(PyObject *module);
#ifdef LIMITED_API_AVAILABLE #ifdef LIMITED_API_AVAILABLE
int _PyTestCapi_Init_VectorcallLimited(PyObject *module); int _PyTestCapi_Init_VectorcallLimited(PyObject *module);

View File

@ -4083,6 +4083,9 @@ PyInit__testcapi(void)
if (_PyTestCapi_Init_Exceptions(m) < 0) { if (_PyTestCapi_Init_Exceptions(m) < 0) {
return NULL; return NULL;
} }
if (_PyTestCapi_Init_Code(m) < 0) {
return NULL;
}
#ifndef LIMITED_API_AVAILABLE #ifndef LIMITED_API_AVAILABLE
PyModule_AddObjectRef(m, "LIMITED_API_AVAILABLE", Py_False); PyModule_AddObjectRef(m, "LIMITED_API_AVAILABLE", Py_False);

View File

@ -567,7 +567,8 @@ _PyCode_New(struct _PyCodeConstructor *con)
******************/ ******************/
PyCodeObject * PyCodeObject *
PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount, PyUnstable_Code_NewWithPosOnlyArgs(
int argcount, int posonlyargcount, int kwonlyargcount,
int nlocals, int stacksize, int flags, int nlocals, int stacksize, int flags,
PyObject *code, PyObject *consts, PyObject *names, PyObject *code, PyObject *consts, PyObject *names,
PyObject *varnames, PyObject *freevars, PyObject *cellvars, PyObject *varnames, PyObject *freevars, PyObject *cellvars,
@ -691,7 +692,7 @@ error:
} }
PyCodeObject * PyCodeObject *
PyCode_New(int argcount, int kwonlyargcount, PyUnstable_Code_New(int argcount, int kwonlyargcount,
int nlocals, int stacksize, int flags, int nlocals, int stacksize, int flags,
PyObject *code, PyObject *consts, PyObject *names, PyObject *code, PyObject *consts, PyObject *names,
PyObject *varnames, PyObject *freevars, PyObject *cellvars, PyObject *varnames, PyObject *freevars, PyObject *cellvars,
@ -1371,7 +1372,7 @@ typedef struct {
int 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)) { if (!PyCode_Check(code)) {
PyErr_BadInternalCall(); PyErr_BadInternalCall();
@ -1392,7 +1393,7 @@ _PyCode_GetExtra(PyObject *code, Py_ssize_t index, void **extra)
int 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(); PyInterpreterState *interp = _PyInterpreterState_GET();

View File

@ -108,6 +108,7 @@
<ClCompile Include="..\Modules\_testcapi\long.c" /> <ClCompile Include="..\Modules\_testcapi\long.c" />
<ClCompile Include="..\Modules\_testcapi\structmember.c" /> <ClCompile Include="..\Modules\_testcapi\structmember.c" />
<ClCompile Include="..\Modules\_testcapi\exceptions.c" /> <ClCompile Include="..\Modules\_testcapi\exceptions.c" />
<ClCompile Include="..\Modules\_testcapi\code.c" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ResourceCompile Include="..\PC\python_nt.rc" /> <ResourceCompile Include="..\PC\python_nt.rc" />

View File

@ -54,6 +54,9 @@
<ClCompile Include="..\Modules\_testcapi\exceptions.c"> <ClCompile Include="..\Modules\_testcapi\exceptions.c">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\Modules\_testcapi\code.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ResourceCompile Include="..\PC\python_nt.rc"> <ResourceCompile Include="..\PC\python_nt.rc">

View File

@ -2988,7 +2988,7 @@ format_awaitable_error(PyThreadState *tstate, PyTypeObject *type, int oparg)
Py_ssize_t Py_ssize_t
_PyEval_RequestCodeExtraIndex(freefunc free) PyUnstable_Eval_RequestCodeExtraIndex(freefunc free)
{ {
PyInterpreterState *interp = _PyInterpreterState_GET(); PyInterpreterState *interp = _PyInterpreterState_GET();
Py_ssize_t new_index; Py_ssize_t new_index;