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,,
type,ternaryfunc,3.2,,
type,traverseproc,3.2,,
type,unaryfunc,3.2,,
+type,vectorcallfunc,3.12,,
type,visitproc,3.2,,
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
----------------------
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:
#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;
}
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,
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*
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 */
#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)
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):
"PyUnicode_Translate",
"PyUnicode_Type",
"PyUnicode_WriteChar",
+ "PyVectorcall_Call",
+ "PyVectorcall_NARGS",
"PyWeakref_GetObject",
"PyWeakref_NewProxy",
"PyWeakref_NewRef",
--- /dev/null
+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.
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'
@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*
#include "Python.h"
int _PyTestCapi_Init_Vectorcall(PyObject *module);
+int _PyTestCapi_Init_VectorcallLimited(PyObject *module);
int _PyTestCapi_Init_Heaptype(PyObject *module);
--- /dev/null
+#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;
+}
if (_PyTestCapi_Init_Vectorcall(m) < 0) {
return NULL;
}
+ if (_PyTestCapi_Init_VectorcallLimited(m) < 0) {
+ return NULL;
+ }
if (_PyTestCapi_Init_Heaptype(m) < 0) {
return NULL;
}
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);
+}
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)
<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>
<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>