mirror of
https://github.com/python/cpython
synced 2024-10-06 16:15:35 +00:00
gh-93274: Expose receiving vectorcall in the Limited API (GH-95717)
This commit is contained in:
parent
cc9160a29b
commit
656dad702d
3
Doc/data/stable_abi.dat
generated
3
Doc/data/stable_abi.dat
generated
|
@ -783,6 +783,8 @@ function,PyUnicode_WriteChar,3.7,,
|
|||
type,PyVarObject,3.2,,members
|
||||
member,PyVarObject.ob_base,3.2,,
|
||||
member,PyVarObject.ob_size,3.2,,
|
||||
function,PyVectorcall_Call,3.12,,
|
||||
function,PyVectorcall_NARGS,3.12,,
|
||||
type,PyWeakReference,3.2,,opaque
|
||||
function,PyWeakref_GetObject,3.2,,
|
||||
function,PyWeakref_NewProxy,3.2,,
|
||||
|
@ -883,4 +885,5 @@ type,symtable,3.2,,opaque
|
|||
type,ternaryfunc,3.2,,
|
||||
type,traverseproc,3.2,,
|
||||
type,unaryfunc,3.2,,
|
||||
type,vectorcallfunc,3.12,,
|
||||
type,visitproc,3.2,,
|
||||
|
|
|
@ -426,14 +426,22 @@ New Features
|
|||
an additional metaclass argument.
|
||||
(Contributed by Wenzel Jakob in :gh:`93012`.)
|
||||
|
||||
* (XXX: this should be combined with :gh:`93274` when that is done)
|
||||
* API for creating objects that can be called using
|
||||
:ref:`the vectorcall protocol <vectorcall>` was added to the
|
||||
:ref:`Limited API <stable>`:
|
||||
|
||||
* :const:`Py_TPFLAGS_HAVE_VECTORCALL`
|
||||
* :c:func:`PyVectorcall_NARGS`
|
||||
* :c:func:`PyVectorcall_Call`
|
||||
* :c:type:`vectorcallfunc`
|
||||
|
||||
The :const:`Py_TPFLAGS_HAVE_VECTORCALL` flag is now removed from a class
|
||||
when the class's :py:meth:`~object.__call__` method is reassigned.
|
||||
This makes vectorcall safe to use with mutable types (i.e. heap types
|
||||
without the :const:`immutable <Py_TPFLAGS_IMMUTABLETYPE>` flag).
|
||||
Mutable types that do not override :c:member:`~PyTypeObject.tp_call` now
|
||||
inherit the :const:`Py_TPFLAGS_HAVE_VECTORCALL` flag.
|
||||
(Contributed by Petr Viktorin in :gh:`93012`.)
|
||||
inherit the ``Py_TPFLAGS_HAVE_VECTORCALL`` flag.
|
||||
(Contributed by Petr Viktorin in :gh:`93274`.)
|
||||
|
||||
Porting to Python 3.12
|
||||
----------------------
|
||||
|
|
|
@ -228,6 +228,16 @@ PyAPI_FUNC(PyObject *) PyObject_CallMethodObjArgs(
|
|||
PyObject *name,
|
||||
...);
|
||||
|
||||
/* Given a vectorcall nargsf argument, return the actual number of arguments.
|
||||
* (For use outside the limited API, this is re-defined as a static inline
|
||||
* function in cpython/abstract.h)
|
||||
*/
|
||||
PyAPI_FUNC(Py_ssize_t) PyVectorcall_NARGS(size_t nargsf);
|
||||
|
||||
/* Call "callable" (which must support vectorcall) with positional arguments
|
||||
"tuple" and keyword arguments "dict". "dict" may also be NULL */
|
||||
PyAPI_FUNC(PyObject *) PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *dict);
|
||||
|
||||
|
||||
/* Implemented elsewhere:
|
||||
|
||||
|
|
|
@ -53,8 +53,12 @@ PyAPI_FUNC(PyObject *) _PyObject_MakeTpCall(
|
|||
#define PY_VECTORCALL_ARGUMENTS_OFFSET \
|
||||
(_Py_STATIC_CAST(size_t, 1) << (8 * sizeof(size_t) - 1))
|
||||
|
||||
// PyVectorcall_NARGS() is exported as a function for the stable ABI.
|
||||
// Here (when we are not using the stable ABI), the name is overridden to
|
||||
// call a static inline function for best performance.
|
||||
#define PyVectorcall_NARGS(n) _PyVectorcall_NARGS(n)
|
||||
static inline Py_ssize_t
|
||||
PyVectorcall_NARGS(size_t n)
|
||||
_PyVectorcall_NARGS(size_t n)
|
||||
{
|
||||
return n & ~PY_VECTORCALL_ARGUMENTS_OFFSET;
|
||||
}
|
||||
|
@ -84,10 +88,6 @@ PyAPI_FUNC(PyObject *) PyObject_VectorcallDict(
|
|||
size_t nargsf,
|
||||
PyObject *kwargs);
|
||||
|
||||
/* Call "callable" (which must support vectorcall) with positional arguments
|
||||
"tuple" and keyword arguments "dict". "dict" may also be NULL */
|
||||
PyAPI_FUNC(PyObject *) PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *dict);
|
||||
|
||||
// Same as PyObject_Vectorcall(), except without keyword arguments
|
||||
PyAPI_FUNC(PyObject *) _PyObject_FastCall(
|
||||
PyObject *func,
|
||||
|
|
|
@ -54,9 +54,6 @@ typedef struct _Py_Identifier {
|
|||
typedef int (*getbufferproc)(PyObject *, Py_buffer *, int);
|
||||
typedef void (*releasebufferproc)(PyObject *, Py_buffer *);
|
||||
|
||||
typedef PyObject *(*vectorcallfunc)(PyObject *callable, PyObject *const *args,
|
||||
size_t nargsf, PyObject *kwnames);
|
||||
|
||||
|
||||
typedef struct {
|
||||
/* Number implementations must check *both*
|
||||
|
|
|
@ -228,6 +228,11 @@ typedef int (*initproc)(PyObject *, PyObject *, PyObject *);
|
|||
typedef PyObject *(*newfunc)(PyTypeObject *, PyObject *, PyObject *);
|
||||
typedef PyObject *(*allocfunc)(PyTypeObject *, Py_ssize_t);
|
||||
|
||||
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030c0000 // 3.12
|
||||
typedef PyObject *(*vectorcallfunc)(PyObject *callable, PyObject *const *args,
|
||||
size_t nargsf, PyObject *kwnames);
|
||||
#endif
|
||||
|
||||
typedef struct{
|
||||
int slot; /* slot id, see below */
|
||||
void *pfunc; /* function pointer */
|
||||
|
@ -381,11 +386,13 @@ given type object has a specified feature.
|
|||
#define Py_TPFLAGS_BASETYPE (1UL << 10)
|
||||
|
||||
/* Set if the type implements the vectorcall protocol (PEP 590) */
|
||||
#ifndef Py_LIMITED_API
|
||||
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030C0000
|
||||
#define Py_TPFLAGS_HAVE_VECTORCALL (1UL << 11)
|
||||
#ifndef Py_LIMITED_API
|
||||
// Backwards compatibility alias for API that was provisional in Python 3.8
|
||||
#define _Py_TPFLAGS_HAVE_VECTORCALL Py_TPFLAGS_HAVE_VECTORCALL
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Set if the type is 'ready' -- fully initialized */
|
||||
#define Py_TPFLAGS_READY (1UL << 12)
|
||||
|
|
|
@ -759,6 +759,11 @@ def __call__(self, *args):
|
|||
self.assertEqual(expected, meth(*args1, **kwargs))
|
||||
self.assertEqual(expected, wrapped(*args, **kwargs))
|
||||
|
||||
def test_vectorcall_limited(self):
|
||||
from _testcapi import pyobject_vectorcall
|
||||
obj = _testcapi.LimitedVectorCallClass()
|
||||
self.assertEqual(pyobject_vectorcall(obj, (), ()), "vectorcall called")
|
||||
|
||||
|
||||
class A:
|
||||
def method_two_args(self, x, y):
|
||||
|
|
2
Lib/test/test_stable_abi_ctypes.py
generated
2
Lib/test/test_stable_abi_ctypes.py
generated
|
@ -782,6 +782,8 @@ SYMBOL_NAMES = (
|
|||
"PyUnicode_Translate",
|
||||
"PyUnicode_Type",
|
||||
"PyUnicode_WriteChar",
|
||||
"PyVectorcall_Call",
|
||||
"PyVectorcall_NARGS",
|
||||
"PyWeakref_GetObject",
|
||||
"PyWeakref_NewProxy",
|
||||
"PyWeakref_NewRef",
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
API for implementing vectorcall (:c:data:`Py_TPFLAGS_HAVE_VECTORCALL`,
|
||||
:c:func:`PyVectorcall_NARGS` and :c:func:`PyVectorcall_Call`) was added to
|
||||
the limited API and stable ABI.
|
|
@ -2275,5 +2275,14 @@
|
|||
added = '3.11'
|
||||
[function.PyErr_SetHandledException]
|
||||
added = '3.11'
|
||||
|
||||
[function.PyType_FromMetaclass]
|
||||
added = '3.12'
|
||||
[const.Py_TPFLAGS_HAVE_VECTORCALL]
|
||||
added = '3.12'
|
||||
[function.PyVectorcall_NARGS]
|
||||
added = '3.12'
|
||||
[function.PyVectorcall_Call]
|
||||
added = '3.12'
|
||||
[typedef.vectorcallfunc]
|
||||
added = '3.12'
|
||||
|
|
|
@ -169,7 +169,7 @@
|
|||
@MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c
|
||||
@MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
|
||||
@MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c
|
||||
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c
|
||||
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/vectorcall_limited.c _testcapi/heaptype.c
|
||||
|
||||
# Some testing modules MUST be built as shared libraries.
|
||||
*shared*
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "Python.h"
|
||||
|
||||
int _PyTestCapi_Init_Vectorcall(PyObject *module);
|
||||
int _PyTestCapi_Init_VectorcallLimited(PyObject *module);
|
||||
int _PyTestCapi_Init_Heaptype(PyObject *module);
|
||||
|
|
77
Modules/_testcapi/vectorcall_limited.c
Normal file
77
Modules/_testcapi/vectorcall_limited.c
Normal file
|
@ -0,0 +1,77 @@
|
|||
#define Py_LIMITED_API 0x030c0000 // 3.12
|
||||
#include "parts.h"
|
||||
#include "structmember.h" // PyMemberDef
|
||||
|
||||
/* Test Vectorcall in the limited API */
|
||||
|
||||
static PyObject *
|
||||
LimitedVectorCallClass_tpcall(PyObject *self, PyObject *args, PyObject *kwargs) {
|
||||
return PyUnicode_FromString("tp_call called");
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
LimitedVectorCallClass_vectorcall(PyObject *callable,
|
||||
PyObject *const *args,
|
||||
size_t nargsf,
|
||||
PyObject *kwnames) {
|
||||
return PyUnicode_FromString("vectorcall called");
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
LimitedVectorCallClass_new(PyTypeObject *tp, PyTypeObject *a, PyTypeObject *kw)
|
||||
{
|
||||
PyObject *self = ((allocfunc)PyType_GetSlot(tp, Py_tp_alloc))(tp, 0);
|
||||
if (!self) {
|
||||
return NULL;
|
||||
}
|
||||
*(vectorcallfunc*)((char*)self + sizeof(PyObject)) = (
|
||||
LimitedVectorCallClass_vectorcall);
|
||||
return self;
|
||||
}
|
||||
|
||||
static PyMemberDef LimitedVectorCallClass_members[] = {
|
||||
{"__vectorcalloffset__", T_PYSSIZET, sizeof(PyObject), READONLY},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
static PyType_Slot LimitedVectorallClass_slots[] = {
|
||||
{Py_tp_new, LimitedVectorCallClass_new},
|
||||
{Py_tp_call, LimitedVectorCallClass_tpcall},
|
||||
{Py_tp_members, LimitedVectorCallClass_members},
|
||||
{0},
|
||||
};
|
||||
|
||||
static PyType_Spec LimitedVectorCallClass_spec = {
|
||||
.name = "_testcapi.LimitedVectorCallClass",
|
||||
.basicsize = (int)(sizeof(PyObject) + sizeof(vectorcallfunc)),
|
||||
.flags = Py_TPFLAGS_DEFAULT
|
||||
| Py_TPFLAGS_HAVE_VECTORCALL
|
||||
| Py_TPFLAGS_BASETYPE,
|
||||
.slots = LimitedVectorallClass_slots,
|
||||
};
|
||||
|
||||
static PyMethodDef TestMethods[] = {
|
||||
/* Add module methods here.
|
||||
* (Empty list left here as template/example, since using
|
||||
* PyModule_AddFunctions isn't very common.)
|
||||
*/
|
||||
{NULL},
|
||||
};
|
||||
|
||||
int
|
||||
_PyTestCapi_Init_VectorcallLimited(PyObject *m) {
|
||||
if (PyModule_AddFunctions(m, TestMethods) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
PyObject *LimitedVectorCallClass = PyType_FromModuleAndSpec(
|
||||
m, &LimitedVectorCallClass_spec, NULL);
|
||||
if (!LimitedVectorCallClass) {
|
||||
return -1;
|
||||
}
|
||||
if (PyModule_AddType(m, (PyTypeObject *)LimitedVectorCallClass) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -6865,6 +6865,9 @@ PyInit__testcapi(void)
|
|||
if (_PyTestCapi_Init_Vectorcall(m) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
if (_PyTestCapi_Init_VectorcallLimited(m) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
if (_PyTestCapi_Init_Heaptype(m) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -1047,3 +1047,11 @@ _PyStack_UnpackDict_Free(PyObject *const *stack, Py_ssize_t nargs,
|
|||
PyMem_Free((PyObject **)stack - 1);
|
||||
Py_DECREF(kwnames);
|
||||
}
|
||||
|
||||
// Export for the stable ABI
|
||||
#undef PyVectorcall_NARGS
|
||||
Py_ssize_t
|
||||
PyVectorcall_NARGS(size_t n)
|
||||
{
|
||||
return _PyVectorcall_NARGS(n);
|
||||
}
|
||||
|
|
2
PC/python3dll.c
generated
2
PC/python3dll.c
generated
|
@ -723,6 +723,8 @@ EXPORT_FUNC(PyUnicodeTranslateError_GetStart)
|
|||
EXPORT_FUNC(PyUnicodeTranslateError_SetEnd)
|
||||
EXPORT_FUNC(PyUnicodeTranslateError_SetReason)
|
||||
EXPORT_FUNC(PyUnicodeTranslateError_SetStart)
|
||||
EXPORT_FUNC(PyVectorcall_Call)
|
||||
EXPORT_FUNC(PyVectorcall_NARGS)
|
||||
EXPORT_FUNC(PyWeakref_GetObject)
|
||||
EXPORT_FUNC(PyWeakref_NewProxy)
|
||||
EXPORT_FUNC(PyWeakref_NewRef)
|
||||
|
|
|
@ -95,6 +95,7 @@
|
|||
<ItemGroup>
|
||||
<ClCompile Include="..\Modules\_testcapimodule.c" />
|
||||
<ClCompile Include="..\Modules\_testcapi\vectorcall.c" />
|
||||
<ClCompile Include="..\Modules\_testcapi\vectorcall_limited.c" />
|
||||
<ClCompile Include="..\Modules\_testcapi\heaptype.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
|
|
@ -15,6 +15,9 @@
|
|||
<ClCompile Include="..\Modules\_testcapi\vectorcall.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Modules\_testcapi\vectorcall_limited.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Modules\_testcapi\heaptype.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
|
Loading…
Reference in a new issue