Thanks to visit codestin.com
Credit goes to github.com

Skip to content

gh-91049: Introduce set vectorcall field API for PyFunctionObject #92257

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
9cf96ec
Enable setting vectorcall field on PyFunctionObjects
May 3, 2022
282e3dc
check if vectorcall is nondefault before inlining call
May 3, 2022
edcdcf1
📜🤖 Added by blurb_it.
blurb-it[bot] May 3, 2022
debf5a9
Apply suggestions from code review
adphrost May 3, 2022
473d18d
addressed comments
May 3, 2022
76e8c9d
added to docs
May 3, 2022
3337459
Merge branch 'main' into pyfunctionobject-set-vectorcall-field
Jun 16, 2022
1543f44
updated doc with fix by itamaro
Jun 16, 2022
08082bd
formatting
Jun 16, 2022
ed93327
removed doc from 3.11
Jun 16, 2022
24ffd30
remove lines from 3.11
Jun 16, 2022
89cb4b2
Apply review feedback from vstinner and markshannon
itamaro Jun 21, 2022
3387d4a
Merge branch 'main' into gh-91049-set-vectorcall
itamaro Sep 2, 2022
b0cc28a
Merge branch 'main' into gh-91049-set-vectorcall
itamaro Sep 5, 2022
99e4085
Add deopt on func version in LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN
itamaro Sep 6, 2022
24353c8
write func version to cache keys version when specializing LOAD_ATTR_…
itamaro Sep 6, 2022
60f7769
remove redundant argcount check in LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN …
itamaro Sep 6, 2022
376ee75
Add missing periods in docs
itamaro Sep 6, 2022
56ffc70
move warning comment to the function C API docs
itamaro Sep 6, 2022
5340e87
Merge branch 'main' into pyfunctionobject-set-vectorcall-field
itamaro Sep 6, 2022
a5e9d13
fix race with GH-96519 (removed func_version in LOAD_ATTR_GETATTRIBUT…
itamaro Sep 6, 2022
12faf52
PEP-7 formatting
itamaro Sep 7, 2022
6623470
Address review feedback
itamaro Sep 7, 2022
9149f14
Add test for LOAD_ATTR specialization when overriding vectorcall of _…
itamaro Sep 8, 2022
e732d7e
Improve setvectorcall + specialization testing
itamaro Sep 8, 2022
84874fb
Merge branch 'main' into pyfunctionobject-set-vectorcall-field
itamaro Sep 15, 2022
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
Merge branch 'main' into gh-91049-set-vectorcall
  • Loading branch information
itamaro committed Sep 2, 2022
commit 3387d4ac65b42ea534105ee0716a2b75ae67b96d
26 changes: 24 additions & 2 deletions Doc/whatsnew/3.12.rst
Original file line number Diff line number Diff line change
Expand Up @@ -437,8 +437,30 @@ New Features
an additional metaclass argument.
(Contributed by Wenzel Jakob in :gh:`93012`.)

* Add new function :c:func:`PyFunction_SetVectorcall` to the C API
which sets the vectorcall field of a given :c:type:`PyFunctionObject`
* 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 ``Py_TPFLAGS_HAVE_VECTORCALL`` flag.
(Contributed by Petr Viktorin in :gh:`93274`.)

The :const:`Py_TPFLAGS_MANAGED_DICT` and :const:`Py_TPFLAGS_MANAGED_WEAKREF`
flags have been added. This allows extensions classes to support object
``__dict__`` and weakrefs with less bookkeeping,
using less memory and with faster access.

* Added new function :c:func:`PyFunction_SetVectorcall` to the C API
which sets the vectorcall field of a given :c:type:`PyFunctionObject`.
Warning: extensions using this API must preserve the behavior
of the unaltered function!
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that this document is the right place to put a warning. Why this warning is not in the documentation of the function?

(Contributed by Andrew Frost in :gh:`92257`.)
Expand Down
7 changes: 7 additions & 0 deletions Lib/test/test_call.py
Original file line number Diff line number Diff line change
Expand Up @@ -770,6 +770,13 @@ def f(num): return num + 1
for _ in range(51):
self.assertEqual("overridden", f(num))

@requires_limited_api
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):
pass
Expand Down
18 changes: 18 additions & 0 deletions Modules/_testcapi/vectorcall.c
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,23 @@ test_pyobject_vectorcall(PyObject *self, PyObject *args)
return PyObject_Vectorcall(func, stack, nargs, kwnames);
}

static PyObject *
override_vectorcall(
PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwnames) {
return PyUnicode_FromString("overridden");
}

static PyObject *
function_setvectorcall(PyObject *self, PyObject *func)
{
if (!PyFunction_Check(func)) {
PyErr_BadInternalCall();
return NULL;
}
PyFunction_SetVectorcall((PyFunctionObject *)func, (vectorcallfunc)override_vectorcall);
Py_RETURN_NONE;
}

static PyObject *
test_pyvectorcall_call(PyObject *self, PyObject *args)
{
Expand Down Expand Up @@ -244,6 +261,7 @@ static PyMethodDef TestMethods[] = {
{"pyobject_fastcall", test_pyobject_fastcall, METH_VARARGS},
{"pyobject_fastcalldict", test_pyobject_fastcalldict, METH_VARARGS},
{"pyobject_vectorcall", test_pyobject_vectorcall, METH_VARARGS},
{"function_setvectorcall", function_setvectorcall, METH_O},
{"pyvectorcall_call", test_pyvectorcall_call, METH_VARARGS},
_TESTCAPI_MAKE_VECTORCALL_CLASS_METHODDEF
_TESTCAPI_HAS_VECTORCALL_FLAG_METHODDEF
Expand Down
144 changes: 0 additions & 144 deletions Modules/_testcapimodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -4650,145 +4650,6 @@ raise_SIGINT_then_send_None(PyObject *self, PyObject *args)
}


static int
fastcall_args(PyObject *args, PyObject ***stack, Py_ssize_t *nargs)
{
if (args == Py_None) {
*stack = NULL;
*nargs = 0;
}
else if (PyTuple_Check(args)) {
*stack = ((PyTupleObject *)args)->ob_item;
*nargs = PyTuple_GET_SIZE(args);
}
else {
PyErr_SetString(PyExc_TypeError, "args must be None or a tuple");
return -1;
}
return 0;
}


static PyObject *
test_pyobject_fastcall(PyObject *self, PyObject *args)
{
PyObject *func, *func_args;
PyObject **stack;
Py_ssize_t nargs;

if (!PyArg_ParseTuple(args, "OO", &func, &func_args)) {
return NULL;
}

if (fastcall_args(func_args, &stack, &nargs) < 0) {
return NULL;
}
return _PyObject_FastCall(func, stack, nargs);
}


static PyObject *
test_pyobject_fastcalldict(PyObject *self, PyObject *args)
{
PyObject *func, *func_args, *kwargs;
PyObject **stack;
Py_ssize_t nargs;

if (!PyArg_ParseTuple(args, "OOO", &func, &func_args, &kwargs)) {
return NULL;
}

if (fastcall_args(func_args, &stack, &nargs) < 0) {
return NULL;
}

if (kwargs == Py_None) {
kwargs = NULL;
}
else if (!PyDict_Check(kwargs)) {
PyErr_SetString(PyExc_TypeError, "kwnames must be None or a dict");
return NULL;
}

return PyObject_VectorcallDict(func, stack, nargs, kwargs);
}


static PyObject *
test_pyobject_vectorcall(PyObject *self, PyObject *args)
{
PyObject *func, *func_args, *kwnames = NULL;
PyObject **stack;
Py_ssize_t nargs, nkw;

if (!PyArg_ParseTuple(args, "OOO", &func, &func_args, &kwnames)) {
return NULL;
}

if (fastcall_args(func_args, &stack, &nargs) < 0) {
return NULL;
}

if (kwnames == Py_None) {
kwnames = NULL;
}
else if (PyTuple_Check(kwnames)) {
nkw = PyTuple_GET_SIZE(kwnames);
if (nargs < nkw) {
PyErr_SetString(PyExc_ValueError, "kwnames longer than args");
return NULL;
}
nargs -= nkw;
}
else {
PyErr_SetString(PyExc_TypeError, "kwnames must be None or a tuple");
return NULL;
}
return PyObject_Vectorcall(func, stack, nargs, kwnames);
}


static PyObject *
override_vectorcall(
PyObject *callable, PyObject *const *args, size_t nargsf, PyObject *kwnames) {
return PyUnicode_FromString("overridden");
}

static PyObject *
function_setvectorcall(PyObject *self, PyObject *func)
{
if (!PyFunction_Check(func)) {
PyErr_BadInternalCall();
return NULL;
}
PyFunction_SetVectorcall((PyFunctionObject *)func, (vectorcallfunc)override_vectorcall);
Py_RETURN_NONE;
}

static PyObject *
test_pyvectorcall_call(PyObject *self, PyObject *args)
{
PyObject *func;
PyObject *argstuple;
PyObject *kwargs = NULL;

if (!PyArg_ParseTuple(args, "OO|O", &func, &argstuple, &kwargs)) {
return NULL;
}

if (!PyTuple_Check(argstuple)) {
PyErr_SetString(PyExc_TypeError, "args must be a tuple");
return NULL;
}
if (kwargs != NULL && !PyDict_Check(kwargs)) {
PyErr_SetString(PyExc_TypeError, "kwargs must be a dict");
return NULL;
}

return PyVectorcall_Call(func, argstuple, kwargs);
}


static PyObject*
stack_pointer(PyObject *self, PyObject *args)
{
Expand Down Expand Up @@ -5828,11 +5689,6 @@ static PyMethodDef TestMethods[] = {
{"tracemalloc_get_traceback", tracemalloc_get_traceback, METH_VARARGS},
{"dict_get_version", dict_get_version, METH_VARARGS},
{"raise_SIGINT_then_send_None", raise_SIGINT_then_send_None, METH_VARARGS},
{"pyobject_fastcall", test_pyobject_fastcall, METH_VARARGS},
{"pyobject_fastcalldict", test_pyobject_fastcalldict, METH_VARARGS},
{"pyobject_vectorcall", test_pyobject_vectorcall, METH_VARARGS},
{"function_setvectorcall", function_setvectorcall, METH_O},
{"pyvectorcall_call", test_pyvectorcall_call, METH_VARARGS},
{"stack_pointer", stack_pointer, METH_NOARGS},
#ifdef W_STOPCODE
{"W_STOPCODE", py_w_stopcode, METH_VARARGS},
Expand Down
You are viewing a condensed version of this merge commit. You can view the full changes here.