Skip to main content

Objects/abstract.c (part 6)

Source:

cpython 3.14 @ ab2d84fe1023/Objects/abstract.c

This annotation covers the object calling protocol. See objects_abstract5_detail for PyObject_GetAttr, PyObject_SetAttr, PyObject_GetItem, and PyObject_SetItem.

Map

LinesSymbolRole
1-80PyObject_Callcallable(args, kwargs) — tuple+dict calling
81-160PyObject_VectorcallFast C array calling convention
161-240PyObject_CallNoArgscallable() with no arguments
241-360PyObject_CallOneArgcallable(arg) with one argument
361-500_PyObject_CallMethodobj.method(args...)

Reading

PyObject_Call

// CPython: Objects/abstract.c:280 PyObject_Call
PyObject *
PyObject_Call(PyObject *callable, PyObject *args, PyObject *kwargs)
{
ternaryfunc call = Py_TYPE(callable)->tp_call;
if (call == NULL) {
PyErr_Format(PyExc_TypeError,
"'%.200s' object is not callable",
Py_TYPE(callable)->tp_name);
return NULL;
}
PyObject *result = (*call)(callable, args, kwargs);
return _Py_CheckFunctionResult(tstate, callable, result, NULL);
}

PyObject_Call is the legacy API. It requires a tuple for args and a dict (or NULL) for kwargs. Most internal callers use PyObject_Vectorcall to avoid allocating the tuple.

PyObject_Vectorcall

// CPython: Objects/abstract.c:340 PyObject_Vectorcall
PyObject *
PyObject_Vectorcall(PyObject *callable, PyObject *const *args,
size_t nargsf, PyObject *kwnames)
{
vectorcallfunc func = _PyVectorcall_Function(callable);
if (func == NULL) {
/* Fall back to PyObject_Call: build tuple */
PyObject *argstuple = _PyTuple_FromArray(args, PyVectorcall_NARGS(nargsf));
return PyObject_Call(callable, argstuple, kwdict_from_kwnames(kwnames));
}
return func(callable, args, nargsf, kwnames);
}

PyObject_Vectorcall is the preferred C API for calling objects. It passes arguments as a C array slice. The PY_VECTORCALL_ARGUMENTS_OFFSET flag allows the callee to use args[-1] temporarily. Falls back to PyObject_Call for types without tp_vectorcall.

PyObject_CallNoArgs

// CPython: Objects/abstract.c:380 PyObject_CallNoArgs
PyObject *
PyObject_CallNoArgs(PyObject *func)
{
return PyObject_Vectorcall(func, NULL, 0, NULL);
}

PyObject_CallNoArgs is a thin wrapper. Used everywhere a callable is invoked with no arguments: iter(obj), next(it), obj.__enter__().

PyObject_CallOneArg

// CPython: Objects/abstract.c:400 PyObject_CallOneArg
PyObject *
PyObject_CallOneArg(PyObject *func, PyObject *arg)
{
PyObject *const stack[1] = {arg};
return PyObject_Vectorcall(func, stack, 1 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL);
}

PyObject_CallOneArg(str, obj) is str(obj). The single-element stack array is passed directly without allocation. PY_VECTORCALL_ARGUMENTS_OFFSET allows the C callee to temporarily use stack[-1] for the self slot.

_PyObject_CallMethod

// CPython: Objects/abstract.c:440 _PyObject_CallMethod
PyObject *
_PyObject_CallMethod(PyObject *obj, PyObject *name,
const char *format, ...)
{
PyObject *meth = PyObject_GetAttr(obj, name);
if (meth == NULL) return NULL;
va_list va; va_start(va, format);
PyObject *args = Py_VaBuildValue(format, va);
va_end(va);
PyObject *result = PyObject_Call(meth, args, NULL);
Py_DECREF(meth);
Py_DECREF(args);
return result;
}

_PyObject_CallMethod(d, "get", "OO", key, default) calls d.get(key, default). The format string uses Py_BuildValue syntax. Used extensively in CPython internals to call Python methods from C.

gopy notes

PyObject_Call is objects.Call(callable, args, kwargs) in objects/protocol.go. PyObject_Vectorcall is objects.Vectorcall. CallNoArgs is objects.CallNoArgs. CallOneArg is objects.CallOneArg. _PyObject_CallMethod is objects.CallMethod.