gh-135748: use argument clinic for more socket methods (#135749)

This commit is contained in:
Kumar Aditya 2025-06-20 17:32:37 +05:30 committed by GitHub
parent b881e3db1e
commit c825b5d989
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 242 additions and 78 deletions

View file

@ -7,7 +7,7 @@ preserve
# include "pycore_runtime.h" // _Py_ID()
#endif
#include "pycore_long.h" // _PyLong_UInt16_Converter()
#include "pycore_modsupport.h" // _PyArg_UnpackKeywords()
#include "pycore_modsupport.h" // _PyArg_CheckPositional()
PyDoc_STRVAR(_socket_socket_close__doc__,
"close($self, /)\n"
@ -29,6 +29,170 @@ _socket_socket_close(PyObject *s, PyObject *Py_UNUSED(ignored))
return _socket_socket_close_impl((PySocketSockObject *)s);
}
PyDoc_STRVAR(_socket_socket_send__doc__,
"send($self, data, flags=0, /)\n"
"--\n"
"\n"
"Send a data string to the socket.\n"
"\n"
"For the optional flags argument, see the Unix manual.\n"
"Return the number of bytes sent; this may be less than len(data) if the network is busy.");
#define _SOCKET_SOCKET_SEND_METHODDEF \
{"send", _PyCFunction_CAST(_socket_socket_send), METH_FASTCALL, _socket_socket_send__doc__},
static PyObject *
_socket_socket_send_impl(PySocketSockObject *s, Py_buffer *pbuf, int flags);
static PyObject *
_socket_socket_send(PyObject *s, PyObject *const *args, Py_ssize_t nargs)
{
PyObject *return_value = NULL;
Py_buffer pbuf = {NULL, NULL};
int flags = 0;
if (!_PyArg_CheckPositional("send", nargs, 1, 2)) {
goto exit;
}
if (PyObject_GetBuffer(args[0], &pbuf, PyBUF_SIMPLE) != 0) {
goto exit;
}
if (nargs < 2) {
goto skip_optional;
}
flags = PyLong_AsInt(args[1]);
if (flags == -1 && PyErr_Occurred()) {
goto exit;
}
skip_optional:
return_value = _socket_socket_send_impl((PySocketSockObject *)s, &pbuf, flags);
exit:
/* Cleanup for pbuf */
if (pbuf.obj) {
PyBuffer_Release(&pbuf);
}
return return_value;
}
PyDoc_STRVAR(_socket_socket_sendall__doc__,
"sendall($self, data, flags=0, /)\n"
"--\n"
"\n"
"Send a data string to the socket.\n"
"\n"
"For the optional flags argument, see the Unix manual.\n"
"This calls send() repeatedly until all data is sent.\n"
"If an error occurs, it\'s impossible to tell how much data has been sent.");
#define _SOCKET_SOCKET_SENDALL_METHODDEF \
{"sendall", _PyCFunction_CAST(_socket_socket_sendall), METH_FASTCALL, _socket_socket_sendall__doc__},
static PyObject *
_socket_socket_sendall_impl(PySocketSockObject *s, Py_buffer *pbuf,
int flags);
static PyObject *
_socket_socket_sendall(PyObject *s, PyObject *const *args, Py_ssize_t nargs)
{
PyObject *return_value = NULL;
Py_buffer pbuf = {NULL, NULL};
int flags = 0;
if (!_PyArg_CheckPositional("sendall", nargs, 1, 2)) {
goto exit;
}
if (PyObject_GetBuffer(args[0], &pbuf, PyBUF_SIMPLE) != 0) {
goto exit;
}
if (nargs < 2) {
goto skip_optional;
}
flags = PyLong_AsInt(args[1]);
if (flags == -1 && PyErr_Occurred()) {
goto exit;
}
skip_optional:
return_value = _socket_socket_sendall_impl((PySocketSockObject *)s, &pbuf, flags);
exit:
/* Cleanup for pbuf */
if (pbuf.obj) {
PyBuffer_Release(&pbuf);
}
return return_value;
}
#if defined(CMSG_LEN)
PyDoc_STRVAR(_socket_socket_sendmsg__doc__,
"sendmsg($self, buffers, ancdata=<unrepresentable>, flags=0,\n"
" address=<unrepresentable>, /)\n"
"--\n"
"\n"
"Send normal and ancillary data to the socket.\n"
"\n"
"It gathering the non-ancillary data from a series of buffers\n"
"and concatenating it into a single message.\n"
"The buffers argument specifies the non-ancillary\n"
"data as an iterable of bytes-like objects (e.g. bytes objects).\n"
"The ancdata argument specifies the ancillary data (control messages)\n"
"as an iterable of zero or more tuples (cmsg_level, cmsg_type,\n"
"cmsg_data), where cmsg_level and cmsg_type are integers specifying the\n"
"protocol level and protocol-specific type respectively, and cmsg_data\n"
"is a bytes-like object holding the associated data. The flags\n"
"argument defaults to 0 and has the same meaning as for send(). If\n"
"address is supplied and not None, it sets a destination address for\n"
"the message. The return value is the number of bytes of non-ancillary\n"
"data sent.");
#define _SOCKET_SOCKET_SENDMSG_METHODDEF \
{"sendmsg", _PyCFunction_CAST(_socket_socket_sendmsg), METH_FASTCALL, _socket_socket_sendmsg__doc__},
static PyObject *
_socket_socket_sendmsg_impl(PySocketSockObject *s, PyObject *data_arg,
PyObject *cmsg_arg, int flags,
PyObject *addr_arg);
static PyObject *
_socket_socket_sendmsg(PyObject *s, PyObject *const *args, Py_ssize_t nargs)
{
PyObject *return_value = NULL;
PyObject *data_arg;
PyObject *cmsg_arg = NULL;
int flags = 0;
PyObject *addr_arg = NULL;
if (!_PyArg_CheckPositional("sendmsg", nargs, 1, 4)) {
goto exit;
}
data_arg = args[0];
if (nargs < 2) {
goto skip_optional;
}
cmsg_arg = args[1];
if (nargs < 3) {
goto skip_optional;
}
flags = PyLong_AsInt(args[2]);
if (flags == -1 && PyErr_Occurred()) {
goto exit;
}
if (nargs < 4) {
goto skip_optional;
}
addr_arg = args[3];
skip_optional:
return_value = _socket_socket_sendmsg_impl((PySocketSockObject *)s, data_arg, cmsg_arg, flags, addr_arg);
exit:
return return_value;
}
#endif /* defined(CMSG_LEN) */
static int
sock_initobj_impl(PySocketSockObject *self, int family, int type, int proto,
PyObject *fdobj);
@ -359,6 +523,10 @@ exit:
#endif /* (defined(HAVE_IF_NAMEINDEX) || defined(MS_WINDOWS)) */
#ifndef _SOCKET_SOCKET_SENDMSG_METHODDEF
#define _SOCKET_SOCKET_SENDMSG_METHODDEF
#endif /* !defined(_SOCKET_SOCKET_SENDMSG_METHODDEF) */
#ifndef _SOCKET_INET_NTOA_METHODDEF
#define _SOCKET_INET_NTOA_METHODDEF
#endif /* !defined(_SOCKET_INET_NTOA_METHODDEF) */
@ -370,4 +538,4 @@ exit:
#ifndef _SOCKET_IF_INDEXTONAME_METHODDEF
#define _SOCKET_IF_INDEXTONAME_METHODDEF
#endif /* !defined(_SOCKET_IF_INDEXTONAME_METHODDEF) */
/*[clinic end generated code: output=07776dd21d1e3b56 input=a9049054013a1b77]*/
/*[clinic end generated code: output=0376c46b76ae2bce input=a9049054013a1b77]*/

View file

@ -4592,55 +4592,62 @@ sock_send_impl(PySocketSockObject *s, void *data)
return (ctx->result >= 0);
}
/* s.send(data [,flags]) method */
/*[clinic input]
_socket.socket.send
self as s: self(type="PySocketSockObject *")
data as pbuf: Py_buffer
flags: int = 0
/
Send a data string to the socket.
For the optional flags argument, see the Unix manual.
Return the number of bytes sent; this may be less than len(data) if the network is busy.
[clinic start generated code]*/
static PyObject *
sock_send(PyObject *self, PyObject *args)
{
PySocketSockObject *s = _PySocketSockObject_CAST(self);
_socket_socket_send_impl(PySocketSockObject *s, Py_buffer *pbuf, int flags)
/*[clinic end generated code: output=3ddf83f17d0c875b input=befe7d7790ccb035]*/
int flags = 0;
Py_buffer pbuf;
{
struct sock_send ctx;
if (!PyArg_ParseTuple(args, "y*|i:send", &pbuf, &flags))
return NULL;
if (!IS_SELECTABLE(s)) {
PyBuffer_Release(&pbuf);
return select_error();
}
ctx.buf = pbuf.buf;
ctx.len = pbuf.len;
ctx.buf = pbuf->buf;
ctx.len = pbuf->len;
ctx.flags = flags;
if (sock_call(s, 1, sock_send_impl, &ctx) < 0) {
PyBuffer_Release(&pbuf);
return NULL;
}
PyBuffer_Release(&pbuf);
return PyLong_FromSsize_t(ctx.result);
}
PyDoc_STRVAR(send_doc,
"send(data[, flags]) -> count\n\
\n\
Send a data string to the socket. For the optional flags\n\
argument, see the Unix manual. Return the number of bytes\n\
sent; this may be less than len(data) if the network is busy.");
/*[clinic input]
_socket.socket.sendall
self as s: self(type="PySocketSockObject *")
data as pbuf: Py_buffer
flags: int = 0
/
/* s.sendall(data [,flags]) method */
Send a data string to the socket.
For the optional flags argument, see the Unix manual.
This calls send() repeatedly until all data is sent.
If an error occurs, it's impossible to tell how much data has been sent.
[clinic start generated code]*/
static PyObject *
sock_sendall(PyObject *self, PyObject *args)
{
PySocketSockObject *s = _PySocketSockObject_CAST(self);
_socket_socket_sendall_impl(PySocketSockObject *s, Py_buffer *pbuf,
int flags)
/*[clinic end generated code: output=ec92861424d3faa8 input=732b15b9ca64dce6]*/
{
char *buf;
Py_ssize_t len, n;
int flags = 0;
Py_buffer pbuf;
struct sock_send ctx;
int has_timeout = (s->sock_timeout > 0);
PyTime_t timeout = s->sock_timeout;
@ -4648,13 +4655,10 @@ sock_sendall(PyObject *self, PyObject *args)
int deadline_initialized = 0;
PyObject *res = NULL;
if (!PyArg_ParseTuple(args, "y*|i:sendall", &pbuf, &flags))
return NULL;
buf = pbuf.buf;
len = pbuf.len;
buf = pbuf->buf;
len = pbuf->len;
if (!IS_SELECTABLE(s)) {
PyBuffer_Release(&pbuf);
return select_error();
}
@ -4692,23 +4696,13 @@ sock_sendall(PyObject *self, PyObject *args)
if (PyErr_CheckSignals())
goto done;
} while (len > 0);
PyBuffer_Release(&pbuf);
res = Py_NewRef(Py_None);
done:
PyBuffer_Release(&pbuf);
return res;
}
PyDoc_STRVAR(sendall_doc,
"sendall(data[, flags])\n\
\n\
Send a data string to the socket. For the optional flags\n\
argument, see the Unix manual. This calls send() repeatedly\n\
until all data is sent. If an error occurs, it's impossible\n\
to tell how much data has been sent.");
#ifdef HAVE_SENDTO
struct sock_sendto {
@ -4858,10 +4852,8 @@ sock_sendmsg_iovec(PySocketSockObject *s, PyObject *data_arg,
}
}
for (; ndatabufs < ndataparts; ndatabufs++) {
if (!PyArg_Parse(PySequence_Fast_GET_ITEM(data_fast, ndatabufs),
"y*;sendmsg() argument 1 must be an iterable of "
"bytes-like objects",
&databufs[ndatabufs]))
if (PyObject_GetBuffer(PySequence_Fast_GET_ITEM(data_fast, ndatabufs),
&databufs[ndatabufs], PyBUF_SIMPLE) < 0)
goto finally;
iovs[ndatabufs].iov_base = databufs[ndatabufs].buf;
iovs[ndatabufs].iov_len = databufs[ndatabufs].len;
@ -4883,13 +4875,39 @@ sock_sendmsg_impl(PySocketSockObject *s, void *data)
return (ctx->result >= 0);
}
/* s.sendmsg(buffers[, ancdata[, flags[, address]]]) method */
/*[clinic input]
_socket.socket.sendmsg
self as s: self(type="PySocketSockObject *")
buffers as data_arg: object
ancdata as cmsg_arg: object = NULL
flags: int = 0
address as addr_arg: object = NULL
/
Send normal and ancillary data to the socket.
It gathering the non-ancillary data from a series of buffers
and concatenating it into a single message.
The buffers argument specifies the non-ancillary
data as an iterable of bytes-like objects (e.g. bytes objects).
The ancdata argument specifies the ancillary data (control messages)
as an iterable of zero or more tuples (cmsg_level, cmsg_type,
cmsg_data), where cmsg_level and cmsg_type are integers specifying the
protocol level and protocol-specific type respectively, and cmsg_data
is a bytes-like object holding the associated data. The flags
argument defaults to 0 and has the same meaning as for send(). If
address is supplied and not None, it sets a destination address for
the message. The return value is the number of bytes of non-ancillary
data sent.
[clinic start generated code]*/
static PyObject *
sock_sendmsg(PyObject *self, PyObject *args)
{
PySocketSockObject *s = _PySocketSockObject_CAST(self);
_socket_socket_sendmsg_impl(PySocketSockObject *s, PyObject *data_arg,
PyObject *cmsg_arg, int flags,
PyObject *addr_arg)
/*[clinic end generated code: output=3b4cb1110644ce39 input=479c13d90bd2f88b]*/
{
Py_ssize_t i, ndatabufs = 0, ncmsgs, ncmsgbufs = 0;
Py_buffer *databufs = NULL;
sock_addr_t addrbuf;
@ -4901,16 +4919,10 @@ sock_sendmsg(PyObject *self, PyObject *args)
} *cmsgs = NULL;
void *controlbuf = NULL;
size_t controllen, controllen_last;
int addrlen, flags = 0;
PyObject *data_arg, *cmsg_arg = NULL, *addr_arg = NULL,
*cmsg_fast = NULL, *retval = NULL;
int addrlen;
PyObject *cmsg_fast = NULL, *retval = NULL;
struct sock_sendmsg ctx;
if (!PyArg_ParseTuple(args, "O|OiO:sendmsg",
&data_arg, &cmsg_arg, &flags, &addr_arg)) {
return NULL;
}
memset(&msg, 0, sizeof(msg));
/* Parse destination address. */
@ -5072,22 +5084,6 @@ finally:
return retval;
}
PyDoc_STRVAR(sendmsg_doc,
"sendmsg(buffers[, ancdata[, flags[, address]]]) -> count\n\
\n\
Send normal and ancillary data to the socket, gathering the\n\
non-ancillary data from a series of buffers and concatenating it into\n\
a single message. The buffers argument specifies the non-ancillary\n\
data as an iterable of bytes-like objects (e.g. bytes objects).\n\
The ancdata argument specifies the ancillary data (control messages)\n\
as an iterable of zero or more tuples (cmsg_level, cmsg_type,\n\
cmsg_data), where cmsg_level and cmsg_type are integers specifying the\n\
protocol level and protocol-specific type respectively, and cmsg_data\n\
is a bytes-like object holding the associated data. The flags\n\
argument defaults to 0 and has the same meaning as for send(). If\n\
address is supplied and not None, it sets a destination address for\n\
the message. The return value is the number of bytes of non-ancillary\n\
data sent.");
#endif /* CMSG_LEN */
#ifdef HAVE_SOCKADDR_ALG
@ -5424,8 +5420,8 @@ static PyMethodDef sock_methods[] = {
recvfrom_into_doc
},
#endif
{"send", sock_send, METH_VARARGS, send_doc},
{"sendall", sock_sendall, METH_VARARGS, sendall_doc},
_SOCKET_SOCKET_SEND_METHODDEF
_SOCKET_SOCKET_SENDALL_METHODDEF
#ifdef HAVE_SENDTO
{"sendto", sock_sendto, METH_VARARGS, sendto_doc},
#endif
@ -5445,7 +5441,7 @@ static PyMethodDef sock_methods[] = {
#ifdef CMSG_LEN
{"recvmsg", sock_recvmsg, METH_VARARGS, recvmsg_doc},
{"recvmsg_into", sock_recvmsg_into, METH_VARARGS, recvmsg_into_doc},
{"sendmsg", sock_sendmsg, METH_VARARGS, sendmsg_doc},
_SOCKET_SOCKET_SENDMSG_METHODDEF
#endif
#ifdef HAVE_SOCKADDR_ALG
{